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:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -33,5 +33,5 @@ Thumbs.db
|
||||
# ── u-claw-app runtime binaries (download on build) ──
|
||||
u-claw-app/resources/runtime/
|
||||
|
||||
# ── Bootable USB (moved to separate repo: u-claw-linux) ──
|
||||
bootable/
|
||||
# ── Bootable USB build artifacts (scripts are tracked, downloads are not) ──
|
||||
bootable/.download-cache/
|
||||
|
||||
18
README.md
18
README.md
@@ -68,13 +68,14 @@ U-Claw/ ← 整个拷到 U 盘
|
||||
└── backups/ 备份
|
||||
```
|
||||
|
||||
### Linux 可启动版(独立仓库)
|
||||
### Linux 可启动版
|
||||
|
||||
连操作系统都没有?没关系。可启动版可以让任意电脑从 U 盘直接启动 Ubuntu + AI:
|
||||
|
||||
> **独立仓库:[u-claw-linux](https://github.com/dongsheng123132/u-claw-linux)**
|
||||
- 本仓库内:[`bootable/`](bootable/) 目录(与其他模块完全独立,互不影响)
|
||||
- 独立仓库:[u-claw-linux](https://github.com/dongsheng123132/u-claw-linux)(内容一致,方便单独克隆)
|
||||
|
||||
基于 Ventoy + Ubuntu 24.04 LTS + 持久化存储,4 步 PowerShell 脚本即可制作。详见独立仓库。
|
||||
基于 Ventoy + Ubuntu 24.04 LTS + 持久化存储,在 Windows 上运行 4 步 PowerShell 脚本即可制作。详见 [`bootable/README.md`](bootable/README.md)。
|
||||
|
||||
### 桌面安装版(Electron App)
|
||||
|
||||
@@ -139,7 +140,7 @@ bash Mac-Start.command # Mac 测试
|
||||
| Mac Apple Silicon (M1-M4) | ✅ | 便携版 + 桌面版 |
|
||||
| Mac Intel (x64) | ✅ | 便携版 + 桌面版 |
|
||||
| Windows x64 | 🚧 开发中 | 便携版 + 桌面版 |
|
||||
| Linux x64(可启动 U 盘) | ✅ | [独立仓库](https://github.com/dongsheng123132/u-claw-linux) |
|
||||
| Linux x64(可启动 U 盘) | ✅ | [`bootable/`](bootable/) 目录 |
|
||||
|
||||
欢迎 PR!特别需要:Windows 脚本完善、教程翻译。
|
||||
|
||||
@@ -224,13 +225,14 @@ U-Claw/ ← Copy entire folder to USB
|
||||
└── backups/ Backups
|
||||
```
|
||||
|
||||
### Linux Bootable USB (Separate Repo)
|
||||
### Linux Bootable USB
|
||||
|
||||
No operating system? No problem. Boot any computer from USB into Ubuntu + AI:
|
||||
|
||||
> **Separate repo: [u-claw-linux](https://github.com/dongsheng123132/u-claw-linux)**
|
||||
- In this repo: [`bootable/`](bootable/) directory (fully independent from other modules)
|
||||
- Standalone repo: [u-claw-linux](https://github.com/dongsheng123132/u-claw-linux) (same content, easier to clone separately)
|
||||
|
||||
Based on Ventoy + Ubuntu 24.04 LTS + persistence. 4-step PowerShell scripts. See the separate repo for details.
|
||||
Based on Ventoy + Ubuntu 24.04 LTS + persistence. 4-step PowerShell scripts on Windows. See [`bootable/README.md`](bootable/README.md) for details.
|
||||
|
||||
### Desktop App (Electron)
|
||||
|
||||
@@ -293,7 +295,7 @@ bash Mac-Start.command # Test on Mac
|
||||
| Mac Apple Silicon (M1-M4) | ✅ | Portable + Desktop |
|
||||
| Mac Intel (x64) | ✅ | Portable + Desktop |
|
||||
| Windows x64 | 🚧 In progress | Portable + Desktop |
|
||||
| Linux x64 (Bootable USB) | ✅ | [Separate repo](https://github.com/dongsheng123132/u-claw-linux) |
|
||||
| Linux x64 (Bootable USB) | ✅ | [`bootable/`](bootable/) directory |
|
||||
|
||||
PRs welcome! Especially: Windows scripts, documentation.
|
||||
|
||||
|
||||
101
bootable/1-prepare-usb.ps1
Normal file
101
bootable/1-prepare-usb.ps1
Normal 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
110
bootable/2-download-iso.ps1
Normal 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"
|
||||
142
bootable/3-create-persistence.ps1
Normal file
142
bootable/3-create-persistence.ps1
Normal 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
164
bootable/4-copy-to-usb.ps1
Normal 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
298
bootable/README.md
Normal 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/11,PowerShell 5.1+ |
|
||||
| 目标电脑 | x86_64(Intel / 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`)
|
||||
|
||||
这是整个方案**最关键**的一步:
|
||||
|
||||
- 检测是否安装了 WSL(Windows 子系统 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 盘选 20GB,64GB 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 LTS,npmmirror.com(国内)或 nodejs.org
|
||||
- **OpenClaw**: npm latest,安装到 `/opt/u-claw/`
|
||||
- **完全独立**: 不引用仓库内 `portable/`、`u-claw-app/`、`website/` 的任何文件
|
||||
7
bootable/linux-setup/openclaw.desktop
Normal file
7
bootable/linux-setup/openclaw.desktop
Normal 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;
|
||||
181
bootable/linux-setup/setup-openclaw.sh
Normal file
181
bootable/linux-setup/setup-openclaw.sh
Normal 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 "============================================"
|
||||
82
bootable/linux-setup/start-openclaw.sh
Normal file
82
bootable/linux-setup/start-openclaw.sh
Normal 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"
|
||||
9
bootable/ventoy/ventoy.json
Normal file
9
bootable/ventoy/ventoy.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"persistence": [
|
||||
{
|
||||
"image": "/ubuntu-24.04.2-desktop-amd64.iso",
|
||||
"backend": "/persistence.dat",
|
||||
"autosel": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user