rsclaw 2026.5.1

AI Agent Engine Compatible with OpenClaw
Documentation
# Local build script for rsclaw on Windows
# Builds release binaries with optional cross-compilation.
#
# Usage:
#   .\scripts\build.ps1                           # build for current platform
#   .\scripts\build.ps1 -Targets all              # build all Windows targets
#   .\scripts\build.ps1 -Targets x86_64,arm64     # build specific targets
#   .\scripts\build.ps1 -Clean                    # remove dist/ directory
#
# Prerequisites:
#   - Rust toolchain 1.91+
#   - Visual Studio Build Tools (MSVC)
#   - For Linux/macOS targets: install `cross`
#     cargo install cross --git https://github.com/cross-rs/cross

param(
    [string[]]$Targets = @(),
    [switch]$All,
    [switch]$Clean,
    [switch]$Help
)

$ErrorActionPreference = "Stop"

$Binary = "rsclaw"
$DistDir = "dist"

$WindowsTargets = @(
    "x86_64-pc-windows-msvc",
    "aarch64-pc-windows-msvc"
)
$LinuxTargets = @(
    "x86_64-unknown-linux-gnu",
    "aarch64-unknown-linux-gnu"
)
$MacosTargets = @(
    "x86_64-apple-darwin",
    "aarch64-apple-darwin"
)
$AllTargets = $WindowsTargets + $LinuxTargets + $MacosTargets

# Read version from Cargo.toml [package] section. Matches the first
# `version = "..."` that appears under `[package]` so it's not confused
# by versions of dependencies declared elsewhere in the file.
function Get-CargoVersion {
    if (-not (Test-Path "Cargo.toml")) { return "dev" }
    $content = Get-Content -Raw "Cargo.toml"
    if ($content -match '(?s)\[package\].*?(?:^|\n)\s*version\s*=\s*"([^"]+)"') {
        return $matches[1]
    }
    return "dev"
}
# Match release-cli.yml + build-ui.ps1: prefix the cargo version with "v"
# so the binary's --version output, the artifact filename, and what the
# desktop bundle's sidecar reports all align.
$Version = "v$(Get-CargoVersion)"
$env:RSCLAW_BUILD_VERSION = $Version
$env:RSCLAW_BUILD_DATE = (Get-Date -Format "yyyy-MM-dd")

# Detect host architecture
function Get-HostTarget {
    $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
    switch ($arch) {
        "X64"   { return "x86_64-pc-windows-msvc" }
        "Arm64" { return "aarch64-pc-windows-msvc" }
        default { return "unknown" }
    }
}

$HostTarget = Get-HostTarget

function Write-Log   { param($Msg) Write-Host "[build] $Msg" -ForegroundColor Cyan }
function Write-Ok    { param($Msg) Write-Host "[  ok ] $Msg" -ForegroundColor Green }
function Write-Warn  { param($Msg) Write-Host "[ warn] $Msg" -ForegroundColor Yellow }
function Write-Err   { param($Msg) Write-Host "[ fail] $Msg" -ForegroundColor Red }

function Test-Cross {
    return (Get-Command "cross" -ErrorAction SilentlyContinue) -ne $null
}

function Install-RustTarget {
    param([string]$Target)
    $installed = rustup target list --installed
    if ($installed -notcontains $Target) {
        Write-Log "Installing rustup target: $Target"
        rustup target add $Target
    }
}

function Build-Target {
    param([string]$Target)

    Write-Log "Building $Target ..."
    Install-RustTarget $Target

    $isNative = ($Target -eq $HostTarget) -or ($Target -match "pc-windows-msvc")
    $buildCmd = "cargo"

    if (-not $isNative) {
        if (Test-Cross) {
            $buildCmd = "cross"
        } else {
            Write-Err "Cannot cross-compile $Target without 'cross'. Install: cargo install cross"
            return $false
        }
    }

    & $buildCmd build --release --target $Target
    if ($LASTEXITCODE -ne 0) {
        Write-Err "Build failed: $Target"
        return $false
    }

    Write-Ok "Built: $Target"
    return (Package-Target $Target)
}

function Package-Target {
    param([string]$Target)

    if (-not (Test-Path $DistDir)) {
        New-Item -ItemType Directory -Path $DistDir -Force | Out-Null
    }

    $ext = if ($Target -match "windows") { ".exe" } else { "" }
    $binPath = "target/$Target/release/$Binary$ext"

    if (-not (Test-Path $binPath)) {
        Write-Err "Binary not found: $binPath"
        return $false
    }

    if ($Target -match "windows") {
        $archiveName = "$Binary-$Version-$Target.zip"
        Compress-Archive -Path $binPath -DestinationPath "$DistDir/$archiveName" -Force
    } else {
        $archiveName = "$Binary-$Version-$Target.tar.gz"
        tar czf "$DistDir/$archiveName" -C "target/$Target/release" "$Binary$ext"
    }

    Write-Ok "Packaged: $DistDir/$archiveName"
    return $true
}

function New-Checksums {
    Write-Log "Generating checksums ..."
    $files = Get-ChildItem "$DistDir/$Binary-*" -ErrorAction SilentlyContinue
    if ($files.Count -eq 0) {
        Write-Warn "No artifacts to checksum"
        return
    }

    $checksums = foreach ($f in $files) {
        $hash = (Get-FileHash -Path $f.FullName -Algorithm SHA256).Hash.ToLower()
        "$hash  $($f.Name)"
    }
    $checksums | Set-Content "$DistDir/SHA256SUMS.txt" -Encoding utf8
    Write-Ok "Checksums: $DistDir/SHA256SUMS.txt"
}

# --- Main ---

if ($Help) {
    Write-Host "Usage: build.ps1 [-Targets <target,...>] [-All] [-Clean]"
    Write-Host ""
    Write-Host "Options:"
    Write-Host "  -Targets   Comma-separated targets or groups: all, windows, linux, macos"
    Write-Host "  -All       Build all 6 targets"
    Write-Host "  -Clean     Remove dist/ directory"
    Write-Host ""
    Write-Host "Windows targets: $($WindowsTargets -join ', ')"
    Write-Host "Linux targets:   $($LinuxTargets -join ', ')"
    Write-Host "macOS targets:   $($MacosTargets -join ', ')"
    Write-Host ""
    Write-Host "Host: $HostTarget"
    Write-Host "cross: $(if (Test-Cross) {'available'} else {'not installed'})"
    exit 0
}

if ($Clean) {
    Write-Log "Cleaning dist/ ..."
    Remove-Item -Path $DistDir -Recurse -Force -ErrorAction SilentlyContinue
    Write-Ok "Cleaned"
    exit 0
}

# Resolve targets
$resolvedTargets = @()

if ($All) {
    $resolvedTargets = $AllTargets
} elseif ($Targets.Count -eq 0) {
    $resolvedTargets = @($HostTarget)
} else {
    foreach ($t in $Targets) {
        switch ($t) {
            "all"     { $resolvedTargets += $AllTargets }
            "windows" { $resolvedTargets += $WindowsTargets }
            "linux"   { $resolvedTargets += $LinuxTargets }
            "macos"   { $resolvedTargets += $MacosTargets }
            "x86_64"  { $resolvedTargets += "x86_64-pc-windows-msvc" }
            "arm64"   { $resolvedTargets += "aarch64-pc-windows-msvc" }
            default   { $resolvedTargets += $t }
        }
    }
}

$resolvedTargets = $resolvedTargets | Select-Object -Unique

Write-Log "rsclaw $Version -- building $($resolvedTargets.Count) target(s)"
Write-Log "Host: $HostTarget"
Write-Log "cross: $(if (Test-Cross) {'available'} else {'not installed'})"
Write-Host ""

$failed = 0
foreach ($target in $resolvedTargets) {
    $result = Build-Target $target
    if (-not $result) { $failed++ }
    Write-Host ""
}

New-Checksums

Write-Host ""
Write-Log "Artifacts in ${DistDir}/:"
Get-ChildItem $DistDir | Format-Table Name, @{N="Size";E={"{0:N2} MB" -f ($_.Length / 1MB)}} -AutoSize
Write-Host ""

if ($failed -gt 0) {
    Write-Err "$failed target(s) failed"
    exit 1
}

Write-Ok "All $($resolvedTargets.Count) target(s) built successfully"