feat: add bootable Linux USB module (independent within repo)

Add bootable/ directory with complete toolkit to create a bootable
Ubuntu USB drive with OpenClaw AI:
- 4-step PowerShell scripts for USB preparation on Windows
- Ventoy 1.0.99 boot manager with casper-rw persistence
- Self-contained Linux setup and startup scripts
- Detailed README with architecture, usage guide, and troubleshooting

bootable/ is fully independent - does not reference any files from
portable/, u-claw-app/, or website/. Also maintained as standalone
repo at u-claw-linux for convenience.

Update main README to reference both bootable/ and u-claw-linux repo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hfshfg
2026-03-14 18:35:41 +08:00
parent 44b1afa177
commit b9f37e1e1b
11 changed files with 1106 additions and 10 deletions

101
bootable/1-prepare-usb.ps1 Normal file
View File

@@ -0,0 +1,101 @@
# ============================================================
# U-Claw Bootable USB - Step 1: Prepare USB with Ventoy
# Downloads Ventoy and launches its installer
# ============================================================
#Requires -RunAsAdministrator
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " U-Claw Bootable USB - Step 1: Ventoy" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
# ── Config ──
$VentoyVersion = "1.0.99"
$VentoyUrl = "https://github.com/ventoy/Ventoy/releases/download/v${VentoyVersion}/ventoy-${VentoyVersion}-windows.zip"
$CacheDir = Join-Path $PSScriptRoot ".download-cache"
$VentoyZip = Join-Path $CacheDir "ventoy-${VentoyVersion}-windows.zip"
$VentoyDir = Join-Path $CacheDir "ventoy-${VentoyVersion}"
# ── Create cache directory ──
if (-not (Test-Path $CacheDir)) {
New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null
}
# ── List USB devices ──
Write-Host "[INFO] Detected USB devices:" -ForegroundColor Yellow
Write-Host ""
$usbDisks = Get-Disk | Where-Object { $_.BusType -eq "USB" }
if ($usbDisks.Count -eq 0) {
Write-Host "[ERROR] No USB devices found. Please insert a USB drive and try again." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
$usbDisks | Format-Table Number, FriendlyName, @{
Label = "Size (GB)"
Expression = { [math]::Round($_.Size / 1GB, 1) }
}, PartitionStyle -AutoSize
Write-Host ""
Write-Host "[WARNING] Ventoy will FORMAT the selected USB drive!" -ForegroundColor Red
Write-Host " All data on the drive will be ERASED!" -ForegroundColor Red
Write-Host ""
$confirm = Read-Host "Type YES to continue, or anything else to cancel"
if ($confirm -ne "YES") {
Write-Host "Cancelled." -ForegroundColor Yellow
exit 0
}
# ── Download Ventoy ──
if (Test-Path $VentoyZip) {
Write-Host "[INFO] Ventoy archive already cached, skipping download." -ForegroundColor Green
} else {
Write-Host "[INFO] Downloading Ventoy v${VentoyVersion}..." -ForegroundColor Yellow
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri $VentoyUrl -OutFile $VentoyZip -UseBasicParsing
Write-Host "[OK] Download complete." -ForegroundColor Green
} catch {
Write-Host "[ERROR] Download failed: $_" -ForegroundColor Red
Write-Host " You can manually download from:" -ForegroundColor Yellow
Write-Host " https://github.com/ventoy/Ventoy/releases" -ForegroundColor Yellow
Read-Host "Press Enter to exit"
exit 1
}
}
# ── Extract ──
if (-not (Test-Path $VentoyDir)) {
Write-Host "[INFO] Extracting Ventoy..." -ForegroundColor Yellow
Expand-Archive -Path $VentoyZip -DestinationPath $CacheDir -Force
Write-Host "[OK] Extracted." -ForegroundColor Green
}
# ── Launch Ventoy2Disk ──
$Ventoy2Disk = Join-Path $VentoyDir "Ventoy2Disk.exe"
if (-not (Test-Path $Ventoy2Disk)) {
Write-Host "[ERROR] Ventoy2Disk.exe not found at $Ventoy2Disk" -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
Write-Host ""
Write-Host "[INFO] Launching Ventoy2Disk GUI..." -ForegroundColor Yellow
Write-Host " 1. Select your USB device in the Ventoy GUI" -ForegroundColor White
Write-Host " 2. Click 'Install' to write Ventoy to the USB" -ForegroundColor White
Write-Host " 3. Wait for completion, then close the GUI" -ForegroundColor White
Write-Host ""
Start-Process -FilePath $Ventoy2Disk -Wait
Write-Host ""
Write-Host "[OK] Step 1 complete! Your USB drive now has Ventoy." -ForegroundColor Green
Write-Host " Next: Run .\2-download-iso.ps1" -ForegroundColor Cyan
Write-Host ""
Read-Host "Press Enter to continue"

110
bootable/2-download-iso.ps1 Normal file
View File

@@ -0,0 +1,110 @@
# ============================================================
# U-Claw Bootable USB - Step 2: Download Ubuntu 24.04 ISO
# Downloads and verifies Ubuntu desktop ISO
# ============================================================
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " U-Claw Bootable USB - Step 2: Ubuntu ISO" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
# ── Config ──
$ISOName = "ubuntu-24.04.2-desktop-amd64.iso"
$SHA256Expected = "d6dab0c3a657988501b4bd76f1297c053df710e06e0c3aece60dead24f270b4d"
$CacheDir = Join-Path $PSScriptRoot ".download-cache"
$ISOPath = Join-Path $CacheDir $ISOName
# Mirror list (China mirrors first)
$Mirrors = @(
"https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/24.04.2/$ISOName",
"https://mirrors.aliyun.com/ubuntu-releases/24.04.2/$ISOName",
"https://mirrors.ustc.edu.cn/ubuntu-releases/24.04.2/$ISOName",
"https://releases.ubuntu.com/24.04.2/$ISOName"
)
# ── Create cache directory ──
if (-not (Test-Path $CacheDir)) {
New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null
}
# ── Check existing download ──
if (Test-Path $ISOPath) {
Write-Host "[INFO] ISO file found in cache. Verifying SHA256..." -ForegroundColor Yellow
$hash = (Get-FileHash -Path $ISOPath -Algorithm SHA256).Hash.ToLower()
if ($hash -eq $SHA256Expected) {
Write-Host "[OK] SHA256 verified. ISO is valid." -ForegroundColor Green
Write-Host " Path: $ISOPath" -ForegroundColor White
Write-Host ""
Write-Host " Next: Run .\3-create-persistence.ps1" -ForegroundColor Cyan
Read-Host "Press Enter to continue"
exit 0
} else {
Write-Host "[WARN] SHA256 mismatch! Re-downloading..." -ForegroundColor Red
Write-Host " Expected: $SHA256Expected" -ForegroundColor Gray
Write-Host " Got: $hash" -ForegroundColor Gray
Remove-Item -Path $ISOPath -Force
}
}
# ── Download ISO ──
Write-Host "[INFO] Downloading Ubuntu 24.04.2 Desktop (~5.8 GB)..." -ForegroundColor Yellow
Write-Host " This will take a while depending on your connection." -ForegroundColor Gray
Write-Host ""
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$downloaded = $false
foreach ($mirror in $Mirrors) {
$mirrorHost = ([System.Uri]$mirror).Host
Write-Host " Trying $mirrorHost ..." -ForegroundColor Gray
try {
$ProgressPreference = 'Continue'
Invoke-WebRequest -Uri $mirror -OutFile $ISOPath -UseBasicParsing
$downloaded = $true
Write-Host "[OK] Downloaded from $mirrorHost" -ForegroundColor Green
break
} catch {
Write-Host " Failed: $($_.Exception.Message)" -ForegroundColor DarkGray
if (Test-Path $ISOPath) { Remove-Item -Path $ISOPath -Force }
continue
}
}
if (-not $downloaded) {
Write-Host ""
Write-Host "[ERROR] All download mirrors failed." -ForegroundColor Red
Write-Host " Please download Ubuntu 24.04.2 Desktop ISO manually:" -ForegroundColor Yellow
Write-Host " https://releases.ubuntu.com/24.04.2/" -ForegroundColor Yellow
Write-Host " Save it as: $ISOPath" -ForegroundColor Yellow
Read-Host "Press Enter to exit"
exit 1
}
# ── Verify SHA256 ──
Write-Host "[INFO] Verifying SHA256 checksum..." -ForegroundColor Yellow
$hash = (Get-FileHash -Path $ISOPath -Algorithm SHA256).Hash.ToLower()
if ($hash -eq $SHA256Expected) {
Write-Host "[OK] SHA256 verified! ISO is authentic." -ForegroundColor Green
} else {
Write-Host "[WARN] SHA256 mismatch!" -ForegroundColor Red
Write-Host " Expected: $SHA256Expected" -ForegroundColor Gray
Write-Host " Got: $hash" -ForegroundColor Gray
Write-Host ""
Write-Host " The ISO may be corrupted or a newer version." -ForegroundColor Yellow
Write-Host " You can continue at your own risk, or re-download." -ForegroundColor Yellow
$cont = Read-Host "Continue anyway? (y/N)"
if ($cont -ne "y" -and $cont -ne "Y") {
Remove-Item -Path $ISOPath -Force
exit 1
}
}
Write-Host ""
Write-Host "[OK] Step 2 complete! ISO saved to:" -ForegroundColor Green
Write-Host " $ISOPath" -ForegroundColor White
Write-Host ""
Write-Host " Next: Run .\3-create-persistence.ps1" -ForegroundColor Cyan
Read-Host "Press Enter to continue"

View File

@@ -0,0 +1,142 @@
# ============================================================
# U-Claw Bootable USB - Step 3: Create Persistence Image
# Creates persistence.dat for Ventoy
# ============================================================
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " U-Claw Bootable USB - Step 3: Persistence" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
# ── Config ──
$CacheDir = Join-Path $PSScriptRoot ".download-cache"
$PersistencePath = Join-Path $CacheDir "persistence.dat"
$DefaultSizeGB = 20
if (-not (Test-Path $CacheDir)) {
New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null
}
# ── Check if persistence.dat already exists ──
if (Test-Path $PersistencePath) {
$existingSize = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
Write-Host "[INFO] persistence.dat already exists (${existingSize} GB)." -ForegroundColor Yellow
$overwrite = Read-Host "Recreate it? (y/N)"
if ($overwrite -ne "y" -and $overwrite -ne "Y") {
Write-Host "[OK] Using existing persistence.dat." -ForegroundColor Green
Write-Host " Next: Run .\4-copy-to-usb.ps1" -ForegroundColor Cyan
Read-Host "Press Enter to continue"
exit 0
}
Remove-Item -Path $PersistencePath -Force
}
# ── Detect usable WSL distro (must have bash + mkfs.ext4) ──
$wslDistro = $null
try {
$distros = (wsl --list --quiet 2>$null) -replace "`0","" | Where-Object { $_.Trim() -ne "" -and $_ -notmatch "docker" }
foreach ($d in $distros) {
$d = $d.Trim()
if ($d) {
$testResult = wsl -d $d -- sh -c "command -v mkfs.ext4 && echo HASEXT4" 2>$null
if ($testResult -match "HASEXT4") {
$wslDistro = $d
break
}
}
}
} catch {}
if ($wslDistro) {
# ── Method A: Use WSL distro to create ext4 image ──
Write-Host "[INFO] Usable WSL distro found: $wslDistro" -ForegroundColor Green
Write-Host ""
$sizeInput = Read-Host "Persistence size in GB (default: $DefaultSizeGB for 32GB USB)"
if ([string]::IsNullOrWhiteSpace($sizeInput)) {
$sizeGB = $DefaultSizeGB
} else {
$sizeGB = [int]$sizeInput
}
if ($sizeGB -lt 1 -or $sizeGB -gt 28) {
Write-Host "[ERROR] Size must be between 1 and 28 GB." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
Write-Host "[INFO] Creating ${sizeGB} GB ext4 image via WSL..." -ForegroundColor Yellow
$sizeMB = $sizeGB * 1024
$tmpFile = "/tmp/uclaw_persistence.dat"
wsl -d $wslDistro -- sh -c "rm -f $tmpFile; dd if=/dev/zero of=$tmpFile bs=1M count=0 seek=$sizeMB 2>/dev/null; mkfs.ext4 -F -L casper-rw $tmpFile 2>/dev/null; echo DONE"
# Copy out via \\wsl$
$wslNetPath = "\\wsl$\$wslDistro\tmp\uclaw_persistence.dat"
if (Test-Path $wslNetPath) {
Write-Host "[INFO] Copying from WSL to cache..." -ForegroundColor Yellow
Copy-Item -Path $wslNetPath -Destination $PersistencePath -Force
wsl -d $wslDistro -- rm -f $tmpFile
}
if (Test-Path $PersistencePath) {
$actualSize = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
Write-Host "[OK] Created ${actualSize} GB persistence image." -ForegroundColor Green
} else {
Write-Host "[ERROR] Failed to create persistence image via WSL." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
} else {
# ── Method B: Create raw file with PowerShell, format in Linux later ──
Write-Host "[INFO] No usable WSL distro (only docker-desktop found or none)." -ForegroundColor Yellow
Write-Host ""
Write-Host " Will create a raw persistence image file." -ForegroundColor White
Write-Host " It will be formatted automatically on first Linux boot." -ForegroundColor White
Write-Host ""
$sizeInput = Read-Host "Persistence size in GB (default: $DefaultSizeGB for 32GB USB)"
if ([string]::IsNullOrWhiteSpace($sizeInput)) {
$sizeGB = $DefaultSizeGB
} else {
$sizeGB = [int]$sizeInput
}
if ($sizeGB -lt 1 -or $sizeGB -gt 28) {
Write-Host "[ERROR] Size must be between 1 and 28 GB." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
Write-Host "[INFO] Creating ${sizeGB} GB sparse file (fast, only allocates on write)..." -ForegroundColor Yellow
# Create sparse file using .NET (instant, doesn't write zeros)
$sizeBytes = [int64]$sizeGB * 1024 * 1024 * 1024
$fs = [System.IO.File]::Create($PersistencePath)
$fs.SetLength($sizeBytes)
$fs.Close()
if (Test-Path $PersistencePath) {
Write-Host "[OK] Created ${sizeGB} GB persistence file." -ForegroundColor Green
Write-Host ""
Write-Host " IMPORTANT: After first boot into Linux, run this to format it:" -ForegroundColor Yellow
Write-Host ' sudo mkfs.ext4 -F -L casper-rw /media/*/Ventoy/persistence.dat' -ForegroundColor White
Write-Host ' Then reboot for persistence to take effect.' -ForegroundColor White
} else {
Write-Host "[ERROR] Failed to create persistence file." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
}
Write-Host ""
Write-Host "[OK] Step 3 complete! Persistence image created." -ForegroundColor Green
Write-Host " Path: $PersistencePath" -ForegroundColor White
Write-Host ""
Write-Host " Next: Run .\4-copy-to-usb.ps1" -ForegroundColor Cyan
Read-Host "Press Enter to continue"

164
bootable/4-copy-to-usb.ps1 Normal file
View File

@@ -0,0 +1,164 @@
# ============================================================
# U-Claw Bootable USB - Step 4: Copy Files to USB
# Copies ISO, persistence, Ventoy config, and setup scripts
# ============================================================
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " U-Claw Bootable USB - Step 4: Copy to USB" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
# ── Config ──
$CacheDir = Join-Path $PSScriptRoot ".download-cache"
$ISOName = "ubuntu-24.04.2-desktop-amd64.iso"
$ISOPath = Join-Path $CacheDir $ISOName
$PersistencePath = Join-Path $CacheDir "persistence.dat"
$VentoyConfigDir = Join-Path $PSScriptRoot "ventoy"
$LinuxSetupDir = Join-Path $PSScriptRoot "linux-setup"
# ── Verify required files ──
$missing = @()
if (-not (Test-Path $ISOPath)) { $missing += "ISO ($ISOPath)" }
if (-not (Test-Path $PersistencePath)) { $missing += "persistence.dat ($PersistencePath)" }
if (-not (Test-Path "$VentoyConfigDir\ventoy.json")) { $missing += "ventoy.json" }
if ($missing.Count -gt 0) {
Write-Host "[ERROR] Missing required files:" -ForegroundColor Red
foreach ($m in $missing) {
Write-Host " - $m" -ForegroundColor Red
}
Write-Host ""
Write-Host " Please run the previous scripts first:" -ForegroundColor Yellow
Write-Host " .\2-download-iso.ps1 and .\3-create-persistence.ps1" -ForegroundColor Yellow
Read-Host "Press Enter to exit"
exit 1
}
# ── Find Ventoy USB drive ──
Write-Host "[INFO] Looking for Ventoy USB drive..." -ForegroundColor Yellow
$ventoyDrive = $null
$volumes = Get-Volume | Where-Object { $_.DriveType -eq "Removable" -or $_.FileSystemLabel -match "(?i)ventoy" }
foreach ($vol in $volumes) {
if ($vol.FileSystemLabel -match "(?i)ventoy" -and $vol.DriveLetter) {
$ventoyDrive = "$($vol.DriveLetter):"
break
}
}
if (-not $ventoyDrive) {
# Fallback: check all removable drives for VENTOY label
$removable = Get-Volume | Where-Object { $_.DriveType -eq "Removable" -and $_.DriveLetter }
if ($removable.Count -eq 0) {
Write-Host "[ERROR] No removable USB drives found." -ForegroundColor Red
Write-Host " Make sure the Ventoy USB is inserted." -ForegroundColor Yellow
Read-Host "Press Enter to exit"
exit 1
}
Write-Host "[INFO] Could not auto-detect Ventoy drive. Available removable drives:" -ForegroundColor Yellow
$removable | Format-Table DriveLetter, FileSystemLabel, @{
Label = "Size (GB)"
Expression = { [math]::Round($_.Size / 1GB, 1) }
} -AutoSize
$driveLetter = Read-Host "Enter the drive letter of your Ventoy USB (e.g., E)"
$ventoyDrive = "${driveLetter}:"
if (-not (Test-Path $ventoyDrive)) {
Write-Host "[ERROR] Drive $ventoyDrive does not exist." -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
}
Write-Host "[OK] Ventoy drive found: $ventoyDrive" -ForegroundColor Green
# ── Check free space ──
$driveInfo = Get-PSDrive -Name $ventoyDrive.TrimEnd(':')
$freeGB = [math]::Round($driveInfo.Free / 1GB, 1)
$isoSizeGB = [math]::Round((Get-Item $ISOPath).Length / 1GB, 1)
$persGB = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
$needGB = $isoSizeGB + $persGB + 0.1
Write-Host "[INFO] Free space: ${freeGB} GB | Need: ~${needGB} GB (ISO: ${isoSizeGB} + Persistence: ${persGB})" -ForegroundColor Yellow
if ($freeGB -lt $needGB) {
Write-Host "[ERROR] Not enough free space on $ventoyDrive" -ForegroundColor Red
Read-Host "Press Enter to exit"
exit 1
}
# ── Confirm ──
Write-Host ""
Write-Host "Will copy to $ventoyDrive :" -ForegroundColor White
Write-Host " - $ISOName (${isoSizeGB} GB)" -ForegroundColor White
Write-Host " - persistence.dat (${persGB} GB)" -ForegroundColor White
Write-Host " - ventoy/ventoy.json" -ForegroundColor White
Write-Host " - u-claw-linux/ (setup scripts)" -ForegroundColor White
Write-Host ""
$confirm = Read-Host "Proceed? (Y/n)"
if ($confirm -eq "n" -or $confirm -eq "N") {
Write-Host "Cancelled." -ForegroundColor Yellow
exit 0
}
# ── Copy files ──
Write-Host ""
Write-Host "[1/4] Copying ISO (~${isoSizeGB} GB, please wait)..." -ForegroundColor Yellow
Copy-Item -Path $ISOPath -Destination "$ventoyDrive\$ISOName" -Force
Write-Host " Done." -ForegroundColor Green
Write-Host "[2/4] Copying persistence.dat (~${persGB} GB)..." -ForegroundColor Yellow
Copy-Item -Path $PersistencePath -Destination "$ventoyDrive\persistence.dat" -Force
Write-Host " Done." -ForegroundColor Green
Write-Host "[3/4] Copying Ventoy configuration..." -ForegroundColor Yellow
$ventoyDestDir = "$ventoyDrive\ventoy"
if (-not (Test-Path $ventoyDestDir)) {
New-Item -ItemType Directory -Path $ventoyDestDir -Force | Out-Null
}
Copy-Item -Path "$VentoyConfigDir\ventoy.json" -Destination "$ventoyDestDir\ventoy.json" -Force
Write-Host " Done." -ForegroundColor Green
Write-Host "[4/4] Copying Linux setup scripts..." -ForegroundColor Yellow
$linuxDestDir = "$ventoyDrive\u-claw-linux"
if (Test-Path $linuxDestDir) {
Remove-Item -Path $linuxDestDir -Recurse -Force
}
Copy-Item -Path $LinuxSetupDir -Destination $linuxDestDir -Recurse -Force
Write-Host " Done." -ForegroundColor Green
# ── Done ──
Write-Host ""
Write-Host "============================================" -ForegroundColor Green
Write-Host " USB Drive Ready!" -ForegroundColor Green
Write-Host "============================================" -ForegroundColor Green
Write-Host ""
Write-Host " Your bootable USB is ready to use!" -ForegroundColor White
Write-Host ""
Write-Host " How to boot:" -ForegroundColor Cyan
Write-Host " 1. Insert the USB into the target computer" -ForegroundColor White
Write-Host " 2. Restart and press the boot key:" -ForegroundColor White
Write-Host " - Dell: F12" -ForegroundColor Gray
Write-Host " - Lenovo: F12" -ForegroundColor Gray
Write-Host " - HP: F9" -ForegroundColor Gray
Write-Host " - ASUS: F2 or DEL" -ForegroundColor Gray
Write-Host " - Acer: F12" -ForegroundColor Gray
Write-Host " - MSI: F11" -ForegroundColor Gray
Write-Host " 3. Select USB device from boot menu" -ForegroundColor White
Write-Host " 4. Ventoy menu -> Select Ubuntu" -ForegroundColor White
Write-Host " 5. Ubuntu boots with persistence enabled" -ForegroundColor White
Write-Host ""
Write-Host " First time in Linux:" -ForegroundColor Cyan
Write-Host " 1. Connect to Wi-Fi" -ForegroundColor White
Write-Host " 2. Open Terminal" -ForegroundColor White
Write-Host ' 3. sudo bash /media/*/Ventoy/u-claw-linux/setup-openclaw.sh' -ForegroundColor White
Write-Host " 4. Double-click 'U-Claw AI Assistant' on desktop" -ForegroundColor White
Write-Host ""
Read-Host "Press Enter to close"

298
bootable/README.md Normal file
View File

@@ -0,0 +1,298 @@
# U-Claw Bootable USB (Linux)
> **把任意电脑变成 AI 工作站 — 插上 U 盘,开机即用**
>
> **Turn any computer into an AI workstation — just boot from USB**
## 独立性说明
本目录 (`bootable/`) 在 u-claw 主仓库中保持**目录级别的独立**
- 不依赖仓库内 `portable/``u-claw-app/``website/` 中的任何文件
- 所有脚本内部硬编码了 URL 和路径,完全自包含
- 出问题只影响 `bootable/` 自身,不会波及其他模块
- 同时维护了一份**独立仓库**[u-claw-linux](https://github.com/dongsheng123132/u-claw-linux),内容一致
## 这是什么
制作一个**可启动的 Linux AI U 盘**
- 插上任意电脑,从 U 盘启动,直接进入 Ubuntu 桌面
- 一键安装 OpenClaw AI 助手,桌面图标双击即用
- 内置持久化存储,安装的软件和数据重启后保留
- **不需要目标电脑有任何操作系统**
> 与便携版(`portable/`)的区别:便携版需要电脑已有 Windows/Mac 系统,可启动版连系统都不需要。
## 技术方案
```
┌────────────────────────────────────────────┐
│ U 盘结构 │
│ │
│ Ventoy 引导区(隐藏分区) │
│ - BIOS + UEFI 双模式启动 │
│ - 开源引导管理器 v1.0.99 │
│ │
│ Ventoy 数据分区(可见) │
│ ubuntu-24.04.2-desktop-amd64.iso 5.8GB │
│ persistence.dat 20GB │
│ ventoy/ventoy.json 配置 │
│ u-claw-linux/ 脚本 │
│ ├── setup-openclaw.sh │
│ └── start-openclaw.sh │
└────────────────────────────────────────────┘
```
**三个核心技术选型:**
| 技术 | 为什么选它 |
|------|-----------|
| **Ventoy 1.0.99** | ISO 文件直接丢进去就能启动,不用烧录,可放多个系统 |
| **Ubuntu 24.04 LTS** | 长期支持版,驱动兼容性最好,社区最大 |
| **casper-rw 持久化** | 让 Live USB 也能保存数据,重启不丢失 |
## 硬件要求
| 项目 | 要求 |
|------|------|
| U 盘 | **32GB+**,强烈推荐 USB 3.0(蓝色接口) |
| 制作环境 | Windows 10/11PowerShell 5.1+ |
| 目标电脑 | x86_64Intel / AMD任意品牌 |
| 网络 | 首次安装 OpenClaw 时需要联网 |
## 快速制作4 步)
在 Windows 上以**管理员身份**打开 PowerShell
```powershell
cd path\to\u-claw\bootable
# Step 1: 下载 Ventoy 并写入 U 盘(会格式化!)
.\1-prepare-usb.ps1
# Step 2: 下载 Ubuntu 24.04 ISO~5.8GB,国内镜像)
.\2-download-iso.ps1
# Step 3: 创建持久化镜像(默认 20GB
.\3-create-persistence.ps1
# Step 4: 拷贝所有文件到 U 盘
.\4-copy-to-usb.ps1
```
## 每一步做了什么
### Step 1: 写入 Ventoy 引导 (`1-prepare-usb.ps1`)
- 列出所有 USB 设备,让你确认
- 从 GitHub 下载 Ventoy 1.0.99
- 启动 Ventoy2Disk.exe GUI
- 你在 GUI 中选择 U 盘 → 点 Install
- **注意:会格式化 U 盘,数据全丢!提前备份!**
### Step 2: 下载 Ubuntu ISO (`2-download-iso.ps1`)
- 从国内镜像下载 Ubuntu 24.04.2 桌面版(~5.8GB
- 镜像优先级:清华 → 阿里 → 中科大 → 官方
- SHA256 校验确保文件完整
- 有缓存,不会重复下载
### Step 3: 创建持久化镜像 (`3-create-persistence.ps1`)
这是整个方案**最关键**的一步:
- 检测是否安装了 WSLWindows 子系统 Linux
- **有 WSL** → 用 `mkfs.ext4` 直接创建格式化好的 ext4 镜像
- **没 WSL** → 创建稀疏文件,首次进 Linux 后需手动格式化
- 卷标必须是 `casper-rw`Ubuntu 持久化的约定)
- 默认 20GB可选 1-28GB
### Step 4: 拷贝到 U 盘 (`4-copy-to-usb.ps1`)
- 自动识别 Ventoy U 盘(通过卷标)
- 检查剩余空间
- 拷贝 4 样东西ISO、persistence.dat、ventoy.json、安装脚本
## 使用方法
### 首次使用
1. 将 U 盘插入目标电脑
2. 开机按启动键:
| 品牌 | 启动键 |
|------|--------|
| Dell 戴尔 | F12 |
| Lenovo 联想 | F12 |
| HP 惠普 | F9 |
| ASUS 华硕 | F2 或 DEL |
| Acer 宏碁 | F12 |
| MSI 微星 | F11 |
| Huawei 华为 | F12 |
| Xiaomi 小米 | F12 |
3. 启动菜单选择 USB 设备
4. Ventoy 菜单 → 选择 Ubuntu
5. 等待 Ubuntu 桌面加载
6. 连接 Wi-Fi
7. 打开终端(`Ctrl+Alt+T` 或右键桌面 → Open Terminal
8. 运行安装命令:
```bash
sudo bash /media/*/Ventoy/u-claw-linux/setup-openclaw.sh
```
9. 桌面出现 **"U-Claw AI Assistant"** 图标
10. 双击图标 → 浏览器打开 → 配置 AI 模型
### 日常使用
1. 插入 U 盘 → 开机选 USB → Ubuntu 桌面
2. 双击桌面图标
3. 所有数据自动保留
## 安装脚本详解 (`setup-openclaw.sh`)
9 个步骤,完全自包含:
| 步骤 | 操作 | 说明 |
|------|------|------|
| 1 | 检查 root 权限 | 必须 `sudo` 运行 |
| 2 | 安装系统依赖 | `curl`, `xdg-utils` |
| 3 | 创建目录 | `/opt/u-claw/{runtime,core,data}` |
| 4 | 下载 Node.js v22 | 国内镜像优先,官方回退 |
| 5 | 创建 package.json | — |
| 6 | 安装 OpenClaw + QQ 插件 | npm 国内镜像 |
| 7 | 写默认配置 | gateway + token |
| 8 | 安装启动脚本 | → `/opt/u-claw/` |
| 9 | 创建桌面快捷方式 | 可选开机自启 |
## 核心配置文件
### `ventoy/ventoy.json`
```json
{
"persistence": [
{
"image": "/ubuntu-24.04.2-desktop-amd64.iso",
"backend": "/persistence.dat",
"autosel": 1
}
]
}
```
告诉 Ventoy启动 Ubuntu ISO 时自动加载 `persistence.dat``autosel: 1` = 不弹确认框。
### Linux 端环境变量
| 变量 | 值 |
|------|-----|
| `OPENCLAW_HOME` | `/opt/u-claw/data/.openclaw` |
| `OPENCLAW_STATE_DIR` | `/opt/u-claw/data/.openclaw` |
| `OPENCLAW_CONFIG_PATH` | `/opt/u-claw/data/.openclaw/openclaw.json` |
## 文件结构
```
bootable/
├── README.md 本文件
├── 1-prepare-usb.ps1 Step 1: Ventoy 写入
├── 2-download-iso.ps1 Step 2: Ubuntu ISO 下载
├── 3-create-persistence.ps1 Step 3: 持久化镜像
├── 4-copy-to-usb.ps1 Step 4: 拷贝到 U 盘
├── linux-setup/
│ ├── setup-openclaw.sh 一键安装 OpenClaw
│ ├── start-openclaw.sh 启动脚本
│ └── openclaw.desktop 桌面快捷方式
└── ventoy/
└── ventoy.json Ventoy 持久化配置
```
## 实践经验与注意事项
### 制作阶段
1. **U 盘选择很重要**
- 必须 32GB+ISO 5.8GB + 持久化 20GB + 系统开销)
- 强烈建议 USB 3.0,否则启动和运行都会很慢
- 推荐品牌:闪迪、金士顿、三星(杂牌盘容易出问题)
- 避免使用 USB Hub直接插主板接口
2. **Step 1 会清空 U 盘**
- Ventoy 安装会格式化整个 U 盘,**务必提前备份**
- 脚本会列出所有 USB 设备让你确认,看清楚再操作
3. **Step 3 持久化镜像**
- 有 WSL → 自动创建 ext4 镜像(最省事)
- 没 WSL → 创建空文件,首次进 Linux 后需手动格式化:
```bash
sudo mkfs.ext4 -F -L casper-rw /media/*/Ventoy/persistence.dat
```
格式化后**必须重启**才能生效
- 大小建议32GB U 盘选 20GB64GB U 盘可选 40GB+
4. **ISO 下载失败**
- 脚本默认走清华/阿里/中科大国内镜像,无需翻墙
- 如果全部失败,手动下载 Ubuntu ISO 放到 `.download-cache/` 目录即可
### 启动阶段
5. **Secure Boot 问题**
- 部分电脑需要关闭 Secure Boot 才能从 U 盘启动
- 进 BIOS → Security → Secure Boot → Disabled
- 不同品牌进 BIOS 的方式不同(通常 DEL 或 F2
6. **找不到 USB 启动项**
- 换个 USB 口试试
- 有些电脑默认禁用了 USB 启动,需要在 BIOS 中开启
- Legacy/CSM 模式和 UEFI 模式都试试
7. **Ubuntu 桌面加载慢**
- 正常现象Live USB 从 U 盘读取比硬盘慢
- USB 3.0 U 盘 + USB 3.0 接口会快很多
- 首次加载约 1-3 分钟
### 使用阶段
8. **Wi-Fi 连接**
- Ubuntu 24.04 支持大多数 Wi-Fi 芯片
- 不行的话用手机 USB 共享网络,或 USB 无线网卡
9. **OpenClaw 安装需要网络**
- 国内镜像优先,无需翻墙
- 安装过程约 1-2 分钟
10. **端口冲突**
- OpenClaw 使用端口 18789-18799
- 提示端口占用 → 关闭终端窗口再重新打开
11. **数据位置**
- 安装目录:`/opt/u-claw/`
- 配置文件:`/opt/u-claw/data/.openclaw/openclaw.json`
- 所有数据保存在持久化镜像中,重启不丢
12. **性能预期**
- U 盘运行比硬盘慢,这是物理限制
- AI 推理在云端,本地只跑网关,对话速度不受影响
## 常见故障排查
| 问题 | 解决方案 |
|------|---------|
| 无法从 U 盘启动 | BIOS 关闭 Secure Boot开启 USB Boot |
| Ventoy 菜单无 Ubuntu | ISO 文件是否在 Ventoy 数据分区根目录 |
| 持久化不生效(重启丢数据) | persistence.dat 是否已格式化为 ext4卷标是否为 `casper-rw` |
| OpenClaw 安装失败 | 检查网络,确认能访问 npmmirror.com |
| 浏览器打不开 | 手动打开浏览器访问 `http://localhost:18789` |
| 屏幕分辨率不对 | Settings → Displays → Resolution |
## 技术说明
- **Ventoy**: 开源引导管理器ISO/WIM/VHD 直接启动,更新 ISO 不用重新格式化
- **Persistence**: Ventoy persistence 插件 + `casper-rw` 标签 ext4 镜像
- **Node.js**: v22.14.0 LTSnpmmirror.com国内或 nodejs.org
- **OpenClaw**: npm latest安装到 `/opt/u-claw/`
- **完全独立**: 不引用仓库内 `portable/`、`u-claw-app/`、`website/` 的任何文件

View File

@@ -0,0 +1,7 @@
[Desktop Entry]
Name=U-Claw AI Assistant
Comment=OpenClaw AI - Plug and Play
Exec=/opt/u-claw/start-openclaw.sh
Terminal=true
Type=Application
Categories=Utility;

View File

@@ -0,0 +1,181 @@
#!/usr/bin/env bash
# ============================================================
# U-Claw OpenClaw One-Click Installer for Linux
# Completely self-contained — no external file dependencies
# ============================================================
set -euo pipefail
# ── All constants defined here (independent of any other project files) ──
NODE_VERSION="v22.14.0"
NODE_MIRROR="https://npmmirror.com/mirrors/node"
NODE_OFFICIAL="https://nodejs.org/dist"
NPM_MIRROR="https://registry.npmmirror.com"
INSTALL_DIR="/opt/u-claw"
NODE_ARCHIVE="node-${NODE_VERSION}-linux-x64.tar.xz"
echo "============================================"
echo " U-Claw OpenClaw Installer for Linux"
echo "============================================"
echo ""
# ── 1. Check root ──
if [[ $EUID -ne 0 ]]; then
echo "[ERROR] This script must be run as root (use sudo)."
echo "Usage: sudo bash setup-openclaw.sh"
exit 1
fi
# Detect the real user (for desktop shortcuts later)
REAL_USER="${SUDO_USER:-$USER}"
REAL_HOME=$(eval echo "~$REAL_USER")
# ── 2. Install system dependencies ──
echo "[1/9] Installing system dependencies..."
apt-get update -qq
apt-get install -y -qq curl xdg-utils > /dev/null 2>&1
echo " Done."
# ── 3. Create install directory ──
echo "[2/9] Creating install directory..."
mkdir -p "$INSTALL_DIR"/{runtime,core,data/{.openclaw,memory,backups,logs}}
echo " $INSTALL_DIR created."
# ── 4. Download and extract Node.js ──
echo "[3/9] Downloading Node.js $NODE_VERSION..."
NODE_DIR="$INSTALL_DIR/runtime/node-linux-x64"
if [[ -x "$NODE_DIR/bin/node" ]]; then
EXISTING_VER=$("$NODE_DIR/bin/node" --version 2>/dev/null || echo "")
if [[ "$EXISTING_VER" == "$NODE_VERSION" ]]; then
echo " Node.js $NODE_VERSION already installed, skipping."
else
echo " Existing Node.js ($EXISTING_VER) differs, re-downloading..."
rm -rf "$NODE_DIR"
fi
fi
if [[ ! -x "$NODE_DIR/bin/node" ]]; then
TMPFILE=$(mktemp /tmp/node-XXXXXX.tar.xz)
# Try China mirror first, then official
if curl -fSL --connect-timeout 10 -o "$TMPFILE" "${NODE_MIRROR}/${NODE_VERSION}/${NODE_ARCHIVE}" 2>/dev/null; then
echo " Downloaded from China mirror."
elif curl -fSL --connect-timeout 10 -o "$TMPFILE" "${NODE_OFFICIAL}/${NODE_VERSION}/${NODE_ARCHIVE}" 2>/dev/null; then
echo " Downloaded from official mirror."
else
echo "[ERROR] Failed to download Node.js. Please check your network."
rm -f "$TMPFILE"
exit 1
fi
mkdir -p "$NODE_DIR"
tar -xJf "$TMPFILE" --strip-components=1 -C "$NODE_DIR"
rm -f "$TMPFILE"
echo " Node.js extracted to $NODE_DIR"
fi
NODE_BIN="$NODE_DIR/bin/node"
NPM_BIN="$NODE_DIR/bin/npm"
echo " Node.js version: $($NODE_BIN --version)"
# ── 5. Create package.json ──
echo "[4/9] Creating package.json..."
cat > "$INSTALL_DIR/core/package.json" << 'PKGJSON'
{
"name": "u-claw-linux",
"version": "1.0.0",
"private": true,
"description": "U-Claw OpenClaw Linux runtime"
}
PKGJSON
echo " Done."
# ── 6. Install OpenClaw + QQ plugin ──
echo "[5/9] Installing OpenClaw (this may take 1-2 minutes)..."
cd "$INSTALL_DIR/core"
"$NPM_BIN" install --registry="$NPM_MIRROR" openclaw@latest @sliverp/qqbot@latest 2>&1 | tail -3
echo " OpenClaw installed."
# ── 7. Write default config ──
echo "[6/9] Writing default configuration..."
CONFIG_FILE="$INSTALL_DIR/data/.openclaw/openclaw.json"
if [[ ! -f "$CONFIG_FILE" ]]; then
cat > "$CONFIG_FILE" << 'CONFIGJSON'
{
"gateway": {
"mode": "local",
"auth": {
"token": "uclaw"
}
}
}
CONFIGJSON
echo " Default config written."
else
echo " Config already exists, keeping existing."
fi
# ── 8. Install start script ──
echo "[7/9] Installing startup script..."
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "$SCRIPT_DIR/start-openclaw.sh" ]]; then
cp "$SCRIPT_DIR/start-openclaw.sh" "$INSTALL_DIR/start-openclaw.sh"
else
echo " [WARN] start-openclaw.sh not found next to this script."
echo " You may need to copy it manually to $INSTALL_DIR/"
fi
chmod +x "$INSTALL_DIR/start-openclaw.sh"
echo " Done."
# ── 9. Install desktop shortcut ──
echo "[8/9] Installing desktop shortcut..."
DESKTOP_FILE="$REAL_HOME/Desktop/openclaw.desktop"
APPS_DIR="$REAL_HOME/.local/share/applications"
mkdir -p "$APPS_DIR"
cat > "$DESKTOP_FILE" << 'DESKTOPEOF'
[Desktop Entry]
Name=U-Claw AI Assistant
Comment=OpenClaw AI - Plug and Play
Exec=/opt/u-claw/start-openclaw.sh
Terminal=true
Type=Application
Categories=Utility;
DESKTOPEOF
cp "$DESKTOP_FILE" "$APPS_DIR/openclaw.desktop"
chmod +x "$DESKTOP_FILE"
chown "$REAL_USER:$REAL_USER" "$DESKTOP_FILE"
chown "$REAL_USER:$REAL_USER" "$APPS_DIR/openclaw.desktop"
# Mark as trusted on GNOME
sudo -u "$REAL_USER" gio set "$DESKTOP_FILE" metadata::trusted true 2>/dev/null || true
echo " Desktop shortcut installed."
# ── 10. Optional: autostart ──
echo "[9/9] Setup complete!"
echo ""
read -rp "Enable autostart on login? (y/N): " AUTOSTART
if [[ "$AUTOSTART" =~ ^[Yy]$ ]]; then
AUTOSTART_DIR="$REAL_HOME/.config/autostart"
mkdir -p "$AUTOSTART_DIR"
cp "$APPS_DIR/openclaw.desktop" "$AUTOSTART_DIR/openclaw.desktop"
chown "$REAL_USER:$REAL_USER" "$AUTOSTART_DIR/openclaw.desktop"
echo " Autostart enabled."
fi
# ── Set ownership ──
chown -R "$REAL_USER:$REAL_USER" "$INSTALL_DIR/data"
echo ""
echo "============================================"
echo " Installation Complete!"
echo "============================================"
echo ""
echo " Node.js: $($NODE_BIN --version)"
echo " Install: $INSTALL_DIR"
echo " Config: $INSTALL_DIR/data/.openclaw/openclaw.json"
echo ""
echo " To start: Double-click 'U-Claw AI Assistant' on desktop"
echo " or: bash $INSTALL_DIR/start-openclaw.sh"
echo ""
echo " First time? Configure your AI model in the browser after startup."
echo "============================================"

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# ============================================================
# U-Claw OpenClaw Startup Script (Linux)
# Completely self-contained — no external file dependencies
# ============================================================
set -euo pipefail
INSTALL_DIR="/opt/u-claw"
NODE_BIN="$INSTALL_DIR/runtime/node-linux-x64/bin/node"
CORE_DIR="$INSTALL_DIR/core"
DATA_DIR="$INSTALL_DIR/data"
export OPENCLAW_HOME="$DATA_DIR/.openclaw"
export OPENCLAW_STATE_DIR="$DATA_DIR/.openclaw"
export OPENCLAW_CONFIG_PATH="$DATA_DIR/.openclaw/openclaw.json"
# --- Sanity checks ---
if [[ ! -x "$NODE_BIN" ]]; then
echo "[ERROR] Node.js not found at $NODE_BIN"
echo "Please run setup-openclaw.sh first."
read -rp "Press Enter to exit..."
exit 1
fi
if [[ ! -d "$CORE_DIR/node_modules/openclaw" ]]; then
echo "[ERROR] OpenClaw not installed in $CORE_DIR"
echo "Please run setup-openclaw.sh first."
read -rp "Press Enter to exit..."
exit 1
fi
# --- Find available port in 18789-18799 ---
PORT=""
for p in $(seq 18789 18799); do
if ! ss -tlnp 2>/dev/null | grep -q ":${p} "; then
PORT="$p"
break
fi
done
if [[ -z "$PORT" ]]; then
echo "[ERROR] All ports 18789-18799 are in use."
read -rp "Press Enter to exit..."
exit 1
fi
echo "============================================"
echo " U-Claw AI Assistant"
echo " Starting on port $PORT ..."
echo "============================================"
# --- Check if model is configured ---
CONFIG_FILE="$OPENCLAW_CONFIG_PATH"
FIRST_RUN=false
if [[ ! -f "$CONFIG_FILE" ]] || ! grep -q '"apiKey"' "$CONFIG_FILE" 2>/dev/null; then
FIRST_RUN=true
echo ""
echo "[INFO] No AI model configured yet."
echo " After startup, please configure your model in the browser."
echo ""
fi
# --- Start OpenClaw gateway ---
cd "$CORE_DIR"
OPENCLAW_ENTRY=$(find node_modules/openclaw -name "openclaw.mjs" -maxdepth 2 2>/dev/null | head -1)
if [[ -z "$OPENCLAW_ENTRY" ]]; then
echo "[ERROR] Cannot find openclaw.mjs entry point."
read -rp "Press Enter to exit..."
exit 1
fi
echo "Opening browser at http://localhost:$PORT ..."
# Open browser after a short delay
(sleep 3 && xdg-open "http://localhost:$PORT" 2>/dev/null) &
"$NODE_BIN" "$OPENCLAW_ENTRY" gateway run \
--allow-unconfigured \
--force \
--port "$PORT"

View File

@@ -0,0 +1,9 @@
{
"persistence": [
{
"image": "/ubuntu-24.04.2-desktop-amd64.iso",
"backend": "/persistence.dat",
"autosel": 1
}
]
}