Skip to content

Instantly share code, notes, and snippets.

@AlexanderDzhoganov
Last active January 23, 2026 08:52
Show Gist options
  • Select an option

  • Save AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f to your computer and use it in GitHub Desktop.

Select an option

Save AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f to your computer and use it in GitHub Desktop.
World Engine Tool Launcher (we) - Download and run WE tools

World Engine Tool Launcher (we)

A simple tool to download, cache, and run World Engine tools from GitHub releases.

Quick Install

macOS / Linux

curl -fsSL https://gist.githubusercontent.com/AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f/raw/we.sh | bash

Then run any tool:

we gui_test
we client --help
we asset_cooker --input ./assets

Windows (PowerShell)

irm https://gist.githubusercontent.com/AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f/raw/we.ps1 | iex

Then run any tool:

we gui_test
we client --help
we asset_cooker --input .\assets

Available Tools

Tool Description
gpu_info Display GPU information (Vulkan-capable GPUs)
gui_test GUI testing and benchmarking application
pkg_viewer Interactive 3D viewer for asset packages
client World Engine game client
asset_cooker Asset processing and packaging tool

Commands

we <app> [args...]     # Run app (auto-updates if new version available)
we update <app>        # Force update an app
we list                # List available apps
we installed           # Show installed apps with versions
we clean               # Clean download cache
we help                # Show help

Configuration

Environment Variable Description Default
WE_HOME Installation directory ~/.world-engine (Unix) or %LOCALAPPDATA%\WorldEngine (Windows)
WE_CONFIG Build configuration release

Using Debug Builds

# Unix
WE_CONFIG=debug we gui_test

# Windows (PowerShell)
$env:WE_CONFIG="debug"; we gui_test

How It Works

  1. First run: Installs the we script to ~/.world-engine/bin/
  2. Running an app:
    • Checks GitHub for latest release
    • Downloads only if there's a new version
    • Caches downloads locally
    • Extracts and runs the app
  3. Subsequent runs: Uses cached version unless newer release available

File Locations

macOS / Linux

~/.world-engine/
├── bin/we           # The launcher script
├── apps/            # Installed applications
│   └── gui_test/
│       ├── version.txt
│       └── gui_test.app/  (macOS)
│       └── gui_test       (Linux)
└── cache/           # Downloaded archives

Windows

%LOCALAPPDATA%\WorldEngine\
├── bin\
│   ├── we.ps1       # PowerShell script
│   └── we.cmd       # CMD wrapper
├── apps\            # Installed applications
│   └── gui_test\
│       ├── version.txt
│       └── gui_test.exe
└── cache\           # Downloaded archives

Adding to PATH

macOS / Linux (bash/zsh)

echo 'export PATH="$HOME/.world-engine/bin:$PATH"' >> ~/.bashrc  # or ~/.zshrc
source ~/.bashrc

Windows (PowerShell)

[Environment]::SetEnvironmentVariable('PATH', $env:PATH + ';' + "$env:LOCALAPPDATA\WorldEngine\bin", 'User')
# Restart terminal

Troubleshooting

macOS: "App is damaged" or Gatekeeper warning

The we script automatically removes quarantine attributes. If you still see warnings:

xattr -cr ~/.world-engine/apps/

Windows: SmartScreen warning

The executables are signed with an EV certificate, so SmartScreen should not block them. If it does, click "More info" → "Run anyway".

Windows Defender performance

For better performance, add an exclusion:

Add-MpPreference -ExclusionPath "$env:LOCALAPPDATA\WorldEngine"

Can't check for updates

Make sure you have internet access. The tool will use cached versions if it can't reach GitHub.

Internal Use Only

This tool is for internal World Engine team use. The GitHub PAT embedded in the scripts has read-only access to releases.

# World Engine Tool Launcher (we)
# Downloads, caches, and runs World Engine tools from GitHub releases
#
# Usage:
# we <app> [args...] Run app (auto-updates if new version available)
# we update <app> Force update an app
# we list List available apps
# we clean Clean download cache
# we help Show this help
#
# One-liner install:
# irm https://gist.githubusercontent.com/USER/GIST_ID/raw/we.ps1 | iex
# we gui_test
#
# ============================================================================
# Configuration
# ============================================================================
$script:REPO = "GoNeuralAI/world-engine"
$script:CLIENT_ID = "Ov23liTexXnTBqdsPp7R"
$script:WE_HOME = if ($env:WE_HOME) { $env:WE_HOME } else { "$env:LOCALAPPDATA\WorldEngine" }
$script:CONFIG = if ($env:WE_CONFIG) { $env:WE_CONFIG } else { "release" }
$script:AVAILABLE_APPS = @("gpu_info", "gui_test", "pkg_viewer", "client", "asset_cooker")
$script:AUTH_FILE = "$script:WE_HOME\auth"
# ============================================================================
# Helper Functions
# ============================================================================
function Write-Log {
param([string]$Message)
Write-Host "[we] $Message" -ForegroundColor Cyan
}
function Write-Err {
param([string]$Message)
Write-Host "[we] ERROR: $Message" -ForegroundColor Red
exit 1
}
function Ensure-Dirs {
$dirs = @(
"$script:WE_HOME\apps",
"$script:WE_HOME\cache",
"$script:WE_HOME\bin"
)
foreach ($dir in $dirs) {
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
}
}
}
# ============================================================================
# GitHub OAuth Device Flow
# ============================================================================
function Get-StoredToken {
if (Test-Path $script:AUTH_FILE) {
return Get-Content $script:AUTH_FILE -Raw
}
return $null
}
function Save-Token {
param([string]$Token)
Ensure-Dirs
$Token | Out-File -FilePath $script:AUTH_FILE -NoNewline -Encoding UTF8
}
function Test-Token {
param([string]$Token)
try {
$response = Invoke-RestMethod -Uri "https://api.github.com/user" `
-Headers @{ "Authorization" = "token $Token" } -ErrorAction Stop
return $true
}
catch {
return $false
}
}
function Invoke-DeviceFlow {
Write-Log "Authenticating with GitHub..."
Write-Log ""
# Step 1: Request device code
try {
$deviceResponse = Invoke-RestMethod -Uri "https://github.com/login/device/code" `
-Method Post `
-Headers @{ "Accept" = "application/json" } `
-Body @{
client_id = $script:CLIENT_ID
scope = "repo"
} -ErrorAction Stop
}
catch {
Write-Err "Failed to initiate device flow: $_"
}
$deviceCode = $deviceResponse.device_code
$userCode = $deviceResponse.user_code
$verificationUri = $deviceResponse.verification_uri
$interval = if ($deviceResponse.interval) { $deviceResponse.interval } else { 5 }
if (-not $deviceCode -or -not $userCode) {
Write-Err "Failed to parse device flow response"
}
# Step 2: Show user the code and open browser
Write-Log "=========================================="
Write-Log " Enter this code: $userCode"
Write-Log "=========================================="
Write-Log ""
Write-Log "Opening browser to: $verificationUri"
Write-Log "If browser doesn't open, visit the URL manually."
Write-Log ""
Start-Process $verificationUri
# Step 3: Poll for authorization
Write-Log "Waiting for authorization..."
while ($true) {
Start-Sleep -Seconds $interval
try {
$tokenResponse = Invoke-RestMethod -Uri "https://github.com/login/oauth/access_token" `
-Method Post `
-Headers @{ "Accept" = "application/json" } `
-Body @{
client_id = $script:CLIENT_ID
device_code = $deviceCode
grant_type = "urn:ietf:params:oauth:grant-type:device_code"
} -ErrorAction Stop
if ($tokenResponse.access_token) {
Save-Token $tokenResponse.access_token
Write-Log "Authentication successful!"
Write-Log ""
return
}
elseif ($tokenResponse.error -eq "authorization_pending") {
continue
}
elseif ($tokenResponse.error -eq "slow_down") {
$interval += 5
continue
}
elseif ($tokenResponse.error -eq "expired_token") {
Write-Err "Authentication timed out. Please try again."
}
elseif ($tokenResponse.error -eq "access_denied") {
Write-Err "Authentication was denied."
}
}
catch {
continue
}
}
}
function Ensure-Auth {
$token = Get-StoredToken
if ($token -and (Test-Token $token)) {
return
}
Invoke-DeviceFlow
}
function Get-Token {
return Get-StoredToken
}
function Get-ReleaseInfo {
param([string]$App)
$token = Get-Token
$headers = @{
"Authorization" = "token $token"
"Accept" = "application/vnd.github.v3+json"
}
try {
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/$script:REPO/releases/tags/$App" `
-Headers $headers -ErrorAction Stop
return $response
}
catch {
return $null
}
}
function Get-VersionFromRelease {
param($ReleaseInfo)
if ($ReleaseInfo -and $ReleaseInfo.name) {
# Extract version from "app_name (version)"
if ($ReleaseInfo.name -match '\(([^)]+)\)') {
return $matches[1]
}
}
return $null
}
function Find-Asset {
param($ReleaseInfo, [string]$Pattern)
foreach ($asset in $ReleaseInfo.assets) {
if ($asset.name -like "*$Pattern*") {
return @{
Id = $asset.id
Name = $asset.name
}
}
}
return $null
}
function Download-Asset {
param([int]$AssetId, [string]$OutputPath)
$token = Get-Token
$headers = @{
"Authorization" = "token $token"
"Accept" = "application/octet-stream"
}
Write-Log "Downloading..."
# Use WebClient for download progress
$webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("Authorization", "token $token")
$webClient.Headers.Add("Accept", "application/octet-stream")
# GitHub API redirects, so we need to follow
$uri = "https://api.github.com/repos/$script:REPO/releases/assets/$AssetId"
try {
# Get redirect URL
$request = [System.Net.HttpWebRequest]::Create($uri)
$request.Headers.Add("Authorization", "token $token")
$request.Accept = "application/octet-stream"
$request.AllowAutoRedirect = $false
$response = $request.GetResponse()
$redirectUrl = $response.GetResponseHeader("Location")
$response.Close()
if ($redirectUrl) {
# Download from redirect URL
Invoke-WebRequest -Uri $redirectUrl -OutFile $OutputPath -UseBasicParsing
}
else {
Write-Err "Failed to get download URL"
}
}
catch {
Write-Err "Download failed: $_"
}
}
function Install-App {
param([string]$App, [string]$Archive)
$appDir = "$script:WE_HOME\apps\$App"
# Clean previous installation
if (Test-Path $appDir) {
Remove-Item -Recurse -Force $appDir
}
New-Item -ItemType Directory -Path $appDir -Force | Out-Null
Write-Log "Extracting..."
Expand-Archive -Path $Archive -DestinationPath $appDir -Force
# Handle Windows Defender
try {
$exclusionPath = $script:WE_HOME
$existingExclusions = Get-MpPreference -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ExclusionPath
if ($existingExclusions -notcontains $exclusionPath) {
Write-Log "Note: You may want to add Windows Defender exclusion for better performance:"
Write-Log " Add-MpPreference -ExclusionPath '$exclusionPath'"
}
}
catch {
# Windows Defender cmdlets not available, skip
}
}
function Get-BinaryPath {
param([string]$App)
return "$script:WE_HOME\apps\$App\$App.exe"
}
# ============================================================================
# Commands
# ============================================================================
function Show-Help {
@"
World Engine Tool Launcher (we)
Usage:
we <app> [args...] Run app (auto-updates if new version available)
we update <app> Force update an app
we list List available apps
we clean Clean download cache
we installed List installed apps with versions
we login Authenticate with GitHub
we logout Remove stored credentials
we help Show this help
Available apps:
gpu_info - GPU information tool
gui_test - GUI testing application
pkg_viewer - Package viewer
client - Game engine client
asset_cooker - Asset processing tool
Environment variables:
WE_HOME Installation directory (default: %LOCALAPPDATA%\WorldEngine)
WE_CONFIG Build config: release or debug (default: release)
Examples:
we gui_test # Run gui_test
we gui_test --help # Pass args to gui_test
`$env:WE_CONFIG="debug"; we client # Run debug build
we update gui_test # Force update
"@
}
function Show-List {
Write-Host "Available apps:"
foreach ($app in $script:AVAILABLE_APPS) {
Write-Host " $app"
}
}
function Show-Installed {
Write-Host "Installed apps:"
foreach ($app in $script:AVAILABLE_APPS) {
$versionFile = "$script:WE_HOME\apps\$app\version.txt"
if (Test-Path $versionFile) {
$version = Get-Content $versionFile -Raw
Write-Host " $app ($($version.Trim()))"
}
}
}
function Invoke-Clean {
Write-Log "Cleaning cache..."
$cachePath = "$script:WE_HOME\cache"
if (Test-Path $cachePath) {
Remove-Item -Recurse -Force $cachePath
}
Write-Log "Cache cleaned"
}
function Invoke-Login {
Invoke-DeviceFlow
}
function Invoke-Logout {
if (Test-Path $script:AUTH_FILE) {
Remove-Item $script:AUTH_FILE
Write-Log "Logged out successfully"
}
else {
Write-Log "Not logged in"
}
}
function Invoke-Update {
param([string]$App)
if (-not $App) {
Write-Err "Usage: we update <app>"
}
# Remove version file to force update
$versionFile = "$script:WE_HOME\apps\$App\version.txt"
if (Test-Path $versionFile) {
Remove-Item $versionFile
}
Invoke-Run -App $App
}
function Invoke-Run {
param(
[string]$App,
[string[]]$AppArgs = @()
)
if (-not $App) {
Show-Help
return
}
# Validate app name
if ($App -notin $script:AVAILABLE_APPS) {
Write-Err "Unknown app: $App. Run 'we list' to see available apps."
}
Ensure-Dirs
Ensure-Auth
$versionFile = "$script:WE_HOME\apps\$App\version.txt"
$localVersion = ""
if (Test-Path $versionFile) {
$localVersion = (Get-Content $versionFile -Raw).Trim()
}
# Get latest release info
Write-Log "Checking for updates..."
$releaseInfo = Get-ReleaseInfo -App $App
if (-not $releaseInfo) {
if ($localVersion) {
Write-Log "Could not check for updates, using cached version"
}
else {
Write-Err "Release not found for $App. The release may not exist yet."
}
}
else {
$latestVersion = Get-VersionFromRelease -ReleaseInfo $releaseInfo
if ($localVersion -ne $latestVersion) {
Write-Log "Updating ${App}: $localVersion -> $latestVersion"
# Find matching asset
$assetPattern = "$App-windows-$script:CONFIG-"
$asset = Find-Asset -ReleaseInfo $releaseInfo -Pattern $assetPattern
if (-not $asset) {
Write-Err "No matching asset found for pattern: $assetPattern"
}
# Download
$cacheFile = "$script:WE_HOME\cache\$($asset.Name)"
New-Item -ItemType Directory -Path "$script:WE_HOME\cache" -Force | Out-Null
Download-Asset -AssetId $asset.Id -OutputPath $cacheFile
# Install
Install-App -App $App -Archive $cacheFile
# Save version
$latestVersion | Out-File -FilePath $versionFile -NoNewline
Write-Log "$App updated to $latestVersion"
}
else {
Write-Log "$App is up to date ($localVersion)"
}
}
# Run the app
$binary = Get-BinaryPath -App $App
if (-not (Test-Path $binary)) {
Write-Err "Binary not found: $binary"
}
& $binary @AppArgs
}
function Add-ToPath {
$binPath = "$script:WE_HOME\bin"
# Check if already in current session PATH
if ($env:PATH -like "*$binPath*") {
Write-Log "PATH already configured"
return
}
# Check if already in persistent User PATH
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if ($userPath -like "*$binPath*") {
Write-Log "PATH export already configured"
# Add to current session
$env:PATH = "$binPath;$env:PATH"
return
}
# Add to persistent User PATH
Write-Log "Adding to PATH..."
if ([string]::IsNullOrEmpty($userPath)) {
[Environment]::SetEnvironmentVariable("PATH", $binPath, "User")
}
else {
[Environment]::SetEnvironmentVariable("PATH", "$binPath;$userPath", "User")
}
# Add to current session
$env:PATH = "$binPath;$env:PATH"
Write-Log "PATH updated. Changes will persist in new terminals."
}
function Invoke-Bootstrap {
Ensure-Dirs
$weScript = "$script:WE_HOME\bin\we.ps1"
$weCmd = "$script:WE_HOME\bin\we.cmd"
Write-Log "Installing 'we' command..."
# Download this script
$gistUrl = "https://gist.githubusercontent.com/AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f/raw/we.ps1"
try {
Invoke-WebRequest -Uri $gistUrl -OutFile $weScript -UseBasicParsing
}
catch {
# If gist URL fails, copy from current invocation if possible
$scriptContent = $MyInvocation.MyCommand.ScriptBlock.ToString()
if ($scriptContent) {
$scriptContent | Out-File -FilePath $weScript -Encoding UTF8
}
}
# Create .cmd wrapper
@"
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0we.ps1" %*
"@ | Out-File -FilePath $weCmd -Encoding ASCII
Write-Log "Installed to: $weScript"
# Auto-add to PATH
Add-ToPath
Write-Log "'we' command is ready to use"
}
# ============================================================================
# Main
# ============================================================================
function Main {
param([string[]]$Arguments)
# If being piped (no args, not interactive), bootstrap
if ($Arguments.Count -eq 0) {
# Check if running interactively
$isInteractive = [Environment]::UserInteractive -and -not $Host.Name.Contains("ISE")
if (-not $isInteractive -or $MyInvocation.InvocationName -eq "&") {
Invoke-Bootstrap
return
}
Show-Help
return
}
$command = $Arguments[0]
$remaining = if ($Arguments.Count -gt 1) { $Arguments[1..($Arguments.Count-1)] } else { @() }
switch ($command) {
{ $_ -in @("help", "--help", "-h", "/?") } {
Show-Help
}
"list" {
Show-List
}
"installed" {
Show-Installed
}
"clean" {
Invoke-Clean
}
"login" {
Invoke-Login
}
"logout" {
Invoke-Logout
}
"update" {
if ($remaining.Count -gt 0) {
Invoke-Update -App $remaining[0]
}
else {
Write-Err "Usage: we update <app>"
}
}
{ $_ -in @("bootstrap", "install") } {
Invoke-Bootstrap
}
default {
Invoke-Run -App $command -AppArgs $remaining
}
}
}
# Handle invocation
if ($MyInvocation.InvocationName -ne ".") {
Main -Arguments $args
}
#!/bin/bash
# World Engine Tool Launcher (we)
# Downloads, caches, and runs World Engine tools from GitHub releases
#
# Usage:
# we <app> [args...] Run app (auto-updates if new version available)
# we update <app> Force update an app
# we list List available apps
# we clean Clean download cache
# we help Show this help
#
# One-liner install:
# curl -fsSL https://gist.githubusercontent.com/USER/GIST_ID/raw/we.sh | bash
# we gui_test
#
set -e
# ============================================================================
# Configuration
# ============================================================================
REPO="GoNeuralAI/world-engine"
CLIENT_ID="Ov23liTexXnTBqdsPp7R"
WE_HOME="${WE_HOME:-$HOME/.world-engine}"
CONFIG="${WE_CONFIG:-release}" # Can be overridden: WE_CONFIG=debug we gui_test
AVAILABLE_APPS="gpu_info gui_test pkg_viewer client asset_cooker"
AUTH_FILE="$WE_HOME/auth"
# ============================================================================
# Detect Platform
# ============================================================================
detect_platform() {
local os
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
darwin) echo "macos" ;;
linux) echo "linux" ;;
*) echo "Unsupported OS: $os" >&2; exit 1 ;;
esac
}
PLATFORM=$(detect_platform)
# ============================================================================
# Helper Functions
# ============================================================================
log() {
echo "[we] $*" >&2
}
error() {
echo "[we] ERROR: $*" >&2
exit 1
}
ensure_dirs() {
mkdir -p "$WE_HOME/apps" "$WE_HOME/cache" "$WE_HOME/bin"
}
# Open URL in browser
open_browser() {
local url="$1"
if [[ "$PLATFORM" == "macos" ]]; then
open "$url"
elif command -v xdg-open &>/dev/null; then
xdg-open "$url"
elif command -v gnome-open &>/dev/null; then
gnome-open "$url"
else
log "Please open this URL in your browser: $url"
fi
}
# ============================================================================
# GitHub OAuth Device Flow
# ============================================================================
get_stored_token() {
if [[ -f "$AUTH_FILE" ]]; then
cat "$AUTH_FILE"
fi
}
store_token() {
local token="$1"
ensure_dirs
echo "$token" > "$AUTH_FILE"
chmod 600 "$AUTH_FILE"
}
validate_token() {
local token="$1"
local response
response=$(curl -sf -H "Authorization: token $token" "https://api.github.com/user" 2>/dev/null) || return 1
echo "$response" | grep -q '"login"' && return 0
return 1
}
do_device_flow() {
log "Authenticating with GitHub..."
log ""
# Step 1: Request device code
local device_response
device_response=$(curl -sf -X POST \
-H "Accept: application/json" \
-d "client_id=$CLIENT_ID&scope=repo" \
"https://github.com/login/device/code")
if [[ -z "$device_response" ]]; then
error "Failed to initiate device flow"
fi
local device_code user_code verification_uri interval
device_code=$(echo "$device_response" | grep -o '"device_code":"[^"]*"' | cut -d'"' -f4)
user_code=$(echo "$device_response" | grep -o '"user_code":"[^"]*"' | cut -d'"' -f4)
verification_uri=$(echo "$device_response" | grep -o '"verification_uri":"[^"]*"' | cut -d'"' -f4)
interval=$(echo "$device_response" | grep -o '"interval":[0-9]*' | cut -d':' -f2)
interval=${interval:-5}
if [[ -z "$device_code" ]] || [[ -z "$user_code" ]]; then
error "Failed to parse device flow response"
fi
# Step 2: Show user the code and open browser
log "=========================================="
log " Enter this code: $user_code"
log "=========================================="
log ""
log "Opening browser to: $verification_uri"
log "If browser doesn't open, visit the URL manually."
log ""
open_browser "$verification_uri"
# Step 3: Poll for authorization
log "Waiting for authorization..."
local token_response access_token error_code
while true; do
sleep "$interval"
token_response=$(curl -sf -X POST \
-H "Accept: application/json" \
-d "client_id=$CLIENT_ID&device_code=$device_code&grant_type=urn:ietf:params:oauth:grant-type:device_code" \
"https://github.com/login/oauth/access_token" 2>/dev/null) || continue
access_token=$(echo "$token_response" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
error_code=$(echo "$token_response" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$access_token" ]]; then
store_token "$access_token"
log "Authentication successful!"
log ""
return 0
elif [[ "$error_code" == "authorization_pending" ]]; then
continue
elif [[ "$error_code" == "slow_down" ]]; then
interval=$((interval + 5))
continue
elif [[ "$error_code" == "expired_token" ]]; then
error "Authentication timed out. Please try again."
elif [[ "$error_code" == "access_denied" ]]; then
error "Authentication was denied."
fi
done
}
ensure_auth() {
local token
token=$(get_stored_token)
if [[ -n "$token" ]] && validate_token "$token"; then
return 0
fi
# Need to authenticate
do_device_flow
}
get_token() {
get_stored_token
}
# Get release info from GitHub API
get_release_info() {
local app="$1"
local token
token=$(get_token)
curl -sfL -H "Authorization: token $token" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPO/releases/tags/$app" 2>/dev/null
}
# Extract version from release name: "app_name (version)" -> "version"
extract_version() {
local release_info="$1"
echo "$release_info" | grep -o '"name":[[:space:]]*"[^"]*"' | head -1 | sed 's/.*(\([^)]*\)).*/\1/'
}
# Find asset ID and name matching pattern
find_asset() {
local release_info="$1"
local pattern="$2"
# Use jq if available, otherwise fall back to grep/sed
if command -v jq &>/dev/null; then
echo "$release_info" | jq -r --arg p "$pattern" \
'.assets[] | select(.name | contains($p)) | "\(.id) \(.name)"' | head -1
else
# Fallback: extract asset info using grep/sed
local assets
assets=$(echo "$release_info" | tr ',' '\n' | grep -A5 '"name":.*'"$pattern")
local id name
id=$(echo "$assets" | grep -o '"id":[[:space:]]*[0-9]*' | head -1 | grep -o '[0-9]*')
name=$(echo "$assets" | grep -o '"name":[[:space:]]*"[^"]*'"$pattern"'[^"]*"' | head -1 | sed 's/"name":[[:space:]]*"//;s/"//')
echo "$id $name"
fi
}
# Download asset from GitHub
download_asset() {
local asset_id="$1"
local output="$2"
local token
token=$(get_token)
log "Downloading..."
curl -#fL -H "Authorization: token $token" \
-H "Accept: application/octet-stream" \
"https://api.github.com/repos/$REPO/releases/assets/$asset_id" \
-o "$output"
}
# Extract and install app
install_app() {
local app="$1"
local archive="$2"
local app_dir="$WE_HOME/apps/$app"
# Clean previous installation
rm -rf "$app_dir"
mkdir -p "$app_dir"
if [[ "$PLATFORM" == "macos" ]]; then
# macOS: Mount DMG, copy .app bundle
log "Mounting DMG..."
local mount_output mount_point
mount_output=$(hdiutil attach "$archive" -nobrowse -readonly 2>&1)
mount_point=$(echo "$mount_output" | grep -o '/Volumes/[^[:space:]]*' | tail -1)
if [[ -z "$mount_point" ]]; then
error "Failed to mount DMG: $mount_output"
fi
log "Copying application..."
cp -R "$mount_point"/*.app "$app_dir/" 2>/dev/null || true
hdiutil detach "$mount_point" -quiet 2>/dev/null || true
# Remove quarantine attribute
log "Removing quarantine attributes..."
xattr -cr "$app_dir" 2>/dev/null || true
else
# Linux: Extract ZIP
log "Extracting..."
unzip -qo "$archive" -d "$app_dir"
chmod +x "$app_dir/$app" 2>/dev/null || true
# Also make any .so files accessible
chmod +x "$app_dir"/*.so 2>/dev/null || true
fi
}
# Get binary path for an app
get_binary_path() {
local app="$1"
local app_dir="$WE_HOME/apps/$app"
if [[ "$PLATFORM" == "macos" ]]; then
echo "$app_dir/$app.app/Contents/MacOS/$app"
else
echo "$app_dir/$app"
fi
}
# ============================================================================
# Commands
# ============================================================================
cmd_help() {
cat <<'EOF'
World Engine Tool Launcher (we)
Usage:
we <app> [args...] Run app (auto-updates if new version available)
we update <app> Force update an app
we list List available apps
we clean Clean download cache
we installed List installed apps with versions
we login Authenticate with GitHub
we logout Remove stored credentials
we help Show this help
Available apps:
gpu_info - GPU information tool
gui_test - GUI testing application
pkg_viewer - Package viewer
client - Game engine client
asset_cooker - Asset processing tool
Environment variables:
WE_HOME Installation directory (default: ~/.world-engine)
WE_CONFIG Build config: release or debug (default: release)
Examples:
we gui_test # Run gui_test
we gui_test --help # Pass args to gui_test
WE_CONFIG=debug we client # Run debug build
we update gui_test # Force update
EOF
}
cmd_list() {
echo "Available apps:"
for app in $AVAILABLE_APPS; do
echo " $app"
done
}
cmd_installed() {
echo "Installed apps:"
for app in $AVAILABLE_APPS; do
local version_file="$WE_HOME/apps/$app/version.txt"
if [[ -f "$version_file" ]]; then
local version
version=$(cat "$version_file")
echo " $app ($version)"
fi
done
}
cmd_clean() {
log "Cleaning cache..."
rm -rf "$WE_HOME/cache"
log "Cache cleaned"
}
cmd_login() {
do_device_flow
}
cmd_logout() {
if [[ -f "$AUTH_FILE" ]]; then
rm -f "$AUTH_FILE"
log "Logged out successfully"
else
log "Not logged in"
fi
}
cmd_update() {
local app="$1"
if [[ -z "$app" ]]; then
error "Usage: we update <app>"
fi
# Remove version file to force update
rm -f "$WE_HOME/apps/$app/version.txt"
cmd_run "$app"
}
cmd_run() {
local app="$1"
shift || true
if [[ -z "$app" ]]; then
cmd_help
exit 1
fi
# Validate app name
if ! echo "$AVAILABLE_APPS" | grep -qw "$app"; then
error "Unknown app: $app. Run 'we list' to see available apps."
fi
ensure_dirs
ensure_auth
local version_file="$WE_HOME/apps/$app/version.txt"
local local_version=""
[[ -f "$version_file" ]] && local_version=$(cat "$version_file")
# Get latest release info
log "Checking for updates..."
local release_info
release_info=$(get_release_info "$app")
if [[ -z "$release_info" ]] || echo "$release_info" | grep -q '"message":.*"Not Found"'; then
if [[ -n "$local_version" ]]; then
log "Could not check for updates, using cached version"
else
error "Release not found for $app. The release may not exist yet."
fi
else
local latest_version
latest_version=$(extract_version "$release_info")
if [[ "$local_version" != "$latest_version" ]]; then
log "Updating $app: $local_version -> $latest_version"
# Find matching asset
local asset_pattern="$app-$PLATFORM-$CONFIG-"
local asset_info
asset_info=$(find_asset "$release_info" "$asset_pattern")
if [[ -z "$asset_info" ]]; then
error "No matching asset found for pattern: $asset_pattern"
fi
local asset_id asset_name
asset_id=$(echo "$asset_info" | awk '{print $1}')
asset_name=$(echo "$asset_info" | awk '{print $2}')
if [[ -z "$asset_id" ]] || [[ -z "$asset_name" ]]; then
error "Failed to parse asset info"
fi
# Download
local cache_file="$WE_HOME/cache/$asset_name"
download_asset "$asset_id" "$cache_file"
# Install
install_app "$app" "$cache_file"
# Save version
echo "$latest_version" > "$version_file"
log "$app updated to $latest_version"
else
log "$app is up to date ($local_version)"
fi
fi
# Run the app
local binary
binary=$(get_binary_path "$app")
if [[ ! -x "$binary" ]]; then
error "Binary not found or not executable: $binary"
fi
exec "$binary" "$@"
}
# ============================================================================
# Bootstrap: Install 'we' command
# ============================================================================
add_to_path() {
local bin_path="$WE_HOME/bin"
local path_export="export PATH=\"\$HOME/.world-engine/bin:\$PATH\""
local shell_name rc_file added=false
# Detect shell and find rc file(s)
shell_name=$(basename "$SHELL")
case "$shell_name" in
zsh)
rc_file="$HOME/.zshrc"
;;
bash)
# On macOS, bash uses .bash_profile for login shells
if [[ "$PLATFORM" == "macos" ]] && [[ -f "$HOME/.bash_profile" ]]; then
rc_file="$HOME/.bash_profile"
else
rc_file="$HOME/.bashrc"
fi
;;
*)
# Fallback to .profile for other shells
rc_file="$HOME/.profile"
;;
esac
# Check if already in PATH
if [[ ":$PATH:" == *":$bin_path:"* ]]; then
log "PATH already configured"
return 0
fi
# Check if export line already exists in rc file
if [[ -f "$rc_file" ]] && grep -q "\.world-engine/bin" "$rc_file" 2>/dev/null; then
log "PATH export already in $rc_file"
# Add to current session
export PATH="$bin_path:$PATH"
return 0
fi
# Add to rc file
log "Adding to PATH in $rc_file..."
echo "" >> "$rc_file"
echo "# World Engine tools" >> "$rc_file"
echo "$path_export" >> "$rc_file"
# Add to current session
export PATH="$bin_path:$PATH"
log "PATH updated. Changes will persist in new terminals."
return 0
}
cmd_bootstrap() {
ensure_dirs
local we_script="$WE_HOME/bin/we"
# Download latest version of this script
log "Installing 'we' command..."
# Copy this script to the bin directory
if [[ -n "${BASH_SOURCE[0]}" ]] && [[ -f "${BASH_SOURCE[0]}" ]]; then
cp "${BASH_SOURCE[0]}" "$we_script"
else
# Being piped, download from gist
curl -sfL "https://gist.githubusercontent.com/AlexanderDzhoganov/33a076f2b90f1f181bcec26dbb8a999f/raw/we.sh" -o "$we_script"
fi
chmod +x "$we_script"
log "Installed to: $we_script"
# Auto-add to PATH
add_to_path
log "'we' command is ready to use"
}
# ============================================================================
# Main
# ============================================================================
main() {
# If no args and script is being piped, bootstrap
if [[ $# -eq 0 ]]; then
if [[ ! -t 0 ]]; then
cmd_bootstrap
exit 0
fi
cmd_help
exit 0
fi
case "$1" in
help|--help|-h)
cmd_help
;;
list)
cmd_list
;;
installed)
cmd_installed
;;
clean)
cmd_clean
;;
login)
cmd_login
;;
logout)
cmd_logout
;;
update)
shift
cmd_update "$@"
;;
bootstrap|install)
cmd_bootstrap
;;
*)
cmd_run "$@"
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment