diff --git a/.gitignore b/.gitignore index 2f8a3be..b7bddd6 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/README.md b/README.md index 3225125..0a7168a 100644 --- a/README.md +++ b/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. diff --git a/bootable/1-prepare-usb.ps1 b/bootable/1-prepare-usb.ps1 new file mode 100644 index 0000000..0afd009 --- /dev/null +++ b/bootable/1-prepare-usb.ps1 @@ -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" diff --git a/bootable/2-download-iso.ps1 b/bootable/2-download-iso.ps1 new file mode 100644 index 0000000..956c43c --- /dev/null +++ b/bootable/2-download-iso.ps1 @@ -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" diff --git a/bootable/3-create-persistence.ps1 b/bootable/3-create-persistence.ps1 new file mode 100644 index 0000000..9aa7883 --- /dev/null +++ b/bootable/3-create-persistence.ps1 @@ -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" diff --git a/bootable/4-copy-to-usb.ps1 b/bootable/4-copy-to-usb.ps1 new file mode 100644 index 0000000..e2aba45 --- /dev/null +++ b/bootable/4-copy-to-usb.ps1 @@ -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" diff --git a/bootable/README.md b/bootable/README.md new file mode 100644 index 0000000..bb79f46 --- /dev/null +++ b/bootable/README.md @@ -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/` 的任何文件 diff --git a/bootable/linux-setup/openclaw.desktop b/bootable/linux-setup/openclaw.desktop new file mode 100644 index 0000000..b8cff9b --- /dev/null +++ b/bootable/linux-setup/openclaw.desktop @@ -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; diff --git a/bootable/linux-setup/setup-openclaw.sh b/bootable/linux-setup/setup-openclaw.sh new file mode 100644 index 0000000..14830c7 --- /dev/null +++ b/bootable/linux-setup/setup-openclaw.sh @@ -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 "============================================" diff --git a/bootable/linux-setup/start-openclaw.sh b/bootable/linux-setup/start-openclaw.sh new file mode 100644 index 0000000..65d28ed --- /dev/null +++ b/bootable/linux-setup/start-openclaw.sh @@ -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" diff --git a/bootable/ventoy/ventoy.json b/bootable/ventoy/ventoy.json new file mode 100644 index 0000000..7b87df5 --- /dev/null +++ b/bootable/ventoy/ventoy.json @@ -0,0 +1,9 @@ +{ + "persistence": [ + { + "image": "/ubuntu-24.04.2-desktop-amd64.iso", + "backend": "/persistence.dat", + "autosel": 1 + } + ] +}