Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
213c4d1
feat(games): add ⚡ Hyperfy installer with Docker containerization
Oct 20, 2025
54dea3f
fix(hyperfy): improve installer with proper environment configuration
Oct 20, 2025
f85dfb3
fix(hyperfy): resolve container startup and build issues
Oct 20, 2025
754587c
feat(hyperfy): add comprehensive environment configuration TUI
Oct 20, 2025
dc01313
refactor(hyperfy): integrate with Omarchy webapp system
Oct 20, 2025
69e51b2
cleanup: remove SVG icon file - using text ⚡ lightning bolt instead
Oct 20, 2025
bc1c0e6
fix(hyperfy): integrate with omarchy webapp system properly
Oct 20, 2025
103aa86
Update Hyperfy integration to use official Docker image and icon
Oct 20, 2025
fdbdcdc
Update Hyperfy menu icon to globe emoji
Oct 20, 2025
fa8b902
Fix template path detection in Hyperfy installer
Oct 20, 2025
5d74e05
Fix Hyperfy webapp name and icon integration
Oct 20, 2025
a3cf4de
Use local Hyperfy logo icon everywhere
Oct 20, 2025
7774dc3
Update Hyperfy icon to full-color version
Oct 20, 2025
0dd1e33
Clean up misplaced applications directory
Oct 20, 2025
8cedec9
Revert "Clean up misplaced applications directory"
Oct 20, 2025
2d0d613
Revert previous cleanup of misplaced applications directory
Oct 20, 2025
4e119ab
Make Hyperfy installer self-contained
Oct 20, 2025
1fa5c3b
Remove external Docker Compose template file
Oct 20, 2025
ee757dd
Embed Hyperfy icon directly in installer script
Oct 20, 2025
5b3f852
Update Hyperfy icon in installer and menu
Oct 20, 2025
b2ae152
Update Hyperfy installation script and removal process
Oct 22, 2025
80d368f
Refactor Hyperfy installation script for improved configuration and u…
Oct 22, 2025
8ae27ff
Merge branch 'basecamp:master' into Hyperfy
HowieDuhzit Oct 22, 2025
b077b3e
Remove WARP.md documentation file and update Hyperfy installation scr…
Oct 22, 2025
a38a31a
Remove unused Hyperfy PNG icon file from the project.
Oct 22, 2025
13ffb2e
Update omarchy-launch-webapp script to simplify command execution by …
Oct 22, 2025
f1bea17
Merge branch 'basecamp:master' into Hyperfy
HowieDuhzit Oct 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added applications/icons/hyperfy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
365 changes: 365 additions & 0 deletions bin/omarchy-install-hyperfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
#!/usr/bin/env bash
set -Eeuo pipefail

# Constants
APP_NAME="Hyperfy"
APP_SLUG="hyperfy"
DEFAULT_PORT="3000"
CONTAINER_NAME_DEFAULT="hyperfy-web"

DATA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/omarchy"
GAMES_ROOT="${DATA_ROOT}/games"
APP_ROOT="${GAMES_ROOT}/${APP_SLUG}"
CONFIG_DIR="${APP_ROOT}/config"
STACK_DIR="${APP_ROOT}/compose"
ENV_FILE="${STACK_DIR}/.env"
COMPOSE_FILE="${STACK_DIR}/docker-compose.yml"

# Helpers
need() { command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }; }
die() { echo "Error: $*" >&2; exit 1; }
header() { gum style --border thick --margin "1" --padding "1 2" --border-foreground 212 "⚡ $*"; }
info() { gum style --foreground 244 "$*"; }

is_port_free() {
local p="$1"
if command -v ss >/dev/null 2>&1; then
! ss -tulpn 2>/dev/null | grep -qE "[:.]${p}\b"
else
! lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -qE "[:.]${p}\b"
fi
}

wait_http_ready() {
local url="$1" timeout="${2:-30}" elapsed=0
until curl -fsS "$url" >/dev/null 2>&1; do
sleep 1
elapsed=$((elapsed+1))
if [ "$elapsed" -ge "$timeout" ]; then return 1; fi
done
}

ensure_docker_running() {
docker info >/dev/null 2>&1 || die "Docker daemon not running. Start it (e.g., 'sudo systemctl start docker') and retry."
}

# Preflight
header "Install ${APP_NAME}"
need gum git docker curl
ensure_docker_running

# Docker image selection
echo
info "Choose Docker image:"
echo "This determines which version of Hyperfy to run. 'dev' is the latest development version with newest features."
IMAGE_CHOICE="$(gum choose --header 'Select Hyperfy Docker image:' 'DEV' 'MAIN' 'CUSTOM')"

case "$IMAGE_CHOICE" in
"DEV")
HYPERFY_IMAGE="ghcr.io/hyperfy-xyz/hyperfy:dev"
;;
"MAIN")
HYPERFY_IMAGE="ghcr.io/hyperfy-xyz/hyperfy:main"
;;
"CUSTOM")
echo "Custom Image Configuration:"
echo "Enter the full Docker image path (e.g., ghcr.io/hyperfy-xyz/hyperfy:latest)"
HYPERFY_IMAGE="$(gum input --prompt 'Docker image: ')"
;;
esac

info "Using Docker image: ${HYPERFY_IMAGE}"

# Basic configuration
echo
echo "Port Configuration:"
echo "This is the port where Hyperfy will be accessible (e.g., http://localhost:3000)"
PORT_INPUT="$(gum input --prompt 'Port to expose (default 3000): ' --value "${DEFAULT_PORT}")"
HYPERFY_PORT="${PORT_INPUT:-$DEFAULT_PORT}"

if ! is_port_free "$HYPERFY_PORT"; then
die "Port ${HYPERFY_PORT} is in use. Choose a free port and re-run."
fi

echo
echo "Container Configuration:"
echo "This is the name of the Docker container (used for management)"
CONTAINER_NAME="$(gum input --prompt 'Container name: ' --value "${CONTAINER_NAME_DEFAULT}")"
[ -n "$CONTAINER_NAME" ] || CONTAINER_NAME="${CONTAINER_NAME_DEFAULT}"

# Hyperfy configuration
echo
info "Configure Hyperfy Environment:"

echo "World Configuration:"
echo "This is the name of the world folder where your Hyperfy world data will be stored"
WORLD_NAME="$(gum input --prompt 'World folder name: ' --value 'world')"

echo
echo "Admin Configuration:"
echo "Set an admin code to restrict admin access, or leave empty to make everyone an admin"
ADMIN_CODE="$(gum input --prompt 'Admin code (leave empty for everyone admin): ')"

echo
echo "Save Configuration:"
echo "How often the world saves automatically (in seconds). Set to 0 to disable auto-saving"
SAVE_INTERVAL="$(gum input --prompt 'Save interval (seconds, 0 to disable): ' --value '60')"

echo
echo "Player Physics:"
echo "Whether players can physically collide with each other in the world"
PLAYER_COLLISION="$(gum choose --header 'Should players collide with other players?' 'true' 'false')"

echo
echo "Upload Limits:"
echo "Maximum file size for model uploads (in MB). Larger files will be rejected"
MAX_UPLOAD_SIZE="$(gum input --prompt 'Max upload size for models (MB): ' --value '12')"

echo
echo "Asset Storage:"
echo "How to store uploaded assets (models, textures, etc.). Local stores on disk, S3 uses cloud storage"
ASSETS_TYPE="$(gum choose --header 'Asset storage type:' 'local' 's3')"
if [[ "$ASSETS_TYPE" == "s3" ]]; then
echo "S3 Configuration:"
echo "S3 bucket URI where assets will be stored (e.g., s3://my-bucket/hyperfy-assets)"
ASSETS_S3_URI="$(gum input --prompt 'S3 URI (s3://bucket/path): ')"
else
ASSETS_S3_URI=""
fi

echo
echo "Database Configuration:"
echo "Local uses SQLite (simple), PostgreSQL is for production or shared databases"
DB_TYPE="$(gum choose --header 'Database type:' 'local' 'postgres')"
if [[ "$DB_TYPE" == "postgres" ]]; then
echo "PostgreSQL Configuration:"
echo "Connection string for your PostgreSQL database"
DB_URI="$(gum input --prompt 'Postgres URI (postgres://user:pass@host:port/db): ')"
echo "Optional database schema (leave empty for default)"
DB_SCHEMA="$(gum input --prompt 'Database schema (optional): ')"
else
DB_URI="local"
DB_SCHEMA=""
fi

echo
echo "System Settings:"
echo "Whether to clean up unused assets when starting (frees disk space but takes time)"
CLEAN_ON_START="$(gum choose --header 'Clean unused assets on startup?' 'true' 'false')"

# Optional AI configuration
echo
info "AI Configuration (optional):"
echo "AI integration allows Hyperfy to use AI models for content generation and assistance"
CONFIGURE_AI="$(gum choose --header 'Configure AI integration?' 'Skip' 'Configure')"
if [[ "$CONFIGURE_AI" == "Configure" ]]; then
AI_PROVIDER="$(gum choose --header 'AI Provider:' 'anthropic' 'openai' 'xai' 'google')"
case "$AI_PROVIDER" in
"anthropic")
AI_MODEL="$(gum choose --header 'Anthropic Model:' 'claude-sonnet-4-20250514' 'claude-opus-4-1-20250805')"
AI_EFFORT="medium"
;;
"openai")
AI_MODEL="$(gum choose --header 'OpenAI Model:' 'gpt-5' 'gpt-5-mini' 'gpt-5-nano')"
AI_EFFORT="$(gum choose --header 'AI Effort:' 'minimal' 'low' 'medium' 'high')"
;;
"xai")
AI_MODEL="grok-4-0709"
AI_EFFORT="medium"
;;
"google")
AI_MODEL="$(gum choose --header 'Google Model:' 'gemini-2.5-pro' 'gemini-2.5-flash')"
AI_EFFORT="medium"
;;
esac
AI_API_KEY="$(gum input --prompt 'AI API Key: ' --password)"
else
AI_PROVIDER="anthropic"
AI_MODEL="claude-sonnet-4-20250514"
AI_EFFORT="medium"
AI_API_KEY=""
fi

# Optional LiveKit configuration
echo
info "LiveKit Voice Chat (optional):"
echo "LiveKit enables real-time voice chat between players in your Hyperfy world"
CONFIGURE_LIVEKIT="$(gum choose --header 'Configure LiveKit voice chat?' 'Skip' 'Configure')"
if [[ "$CONFIGURE_LIVEKIT" == "Configure" ]]; then
LIVEKIT_WS_URL="$(gum input --prompt 'LiveKit WebSocket URL: ')"
LIVEKIT_API_KEY="$(gum input --prompt 'LiveKit API Key: ')"
LIVEKIT_API_SECRET="$(gum input --prompt 'LiveKit API Secret: ' --password)"
else
LIVEKIT_WS_URL=""
LIVEKIT_API_KEY=""
LIVEKIT_API_SECRET=""
fi

gum confirm "Proceed installing ${APP_NAME} with Docker to: ${APP_ROOT} and expose http://localhost:${HYPERFY_PORT} ?" || exit 0

# Create configuration
mkdir -p "${APP_ROOT}" "${STACK_DIR}" "${CONFIG_DIR}"

# Generate Docker Compose file
cat > "${COMPOSE_FILE}" <<EOF
name: omarchy-hyperfy
services:
hyperfy:
image: ${HYPERFY_IMAGE}
container_name: \${HYPERFY_CONTAINER_NAME:-hyperfy-web}
restart: unless-stopped
environment:
- NODE_ENV=development
env_file:
- \${HOST_REPO_DIR}/.env
ports:
- "\${HYPERFY_PORT}:3000"
volumes:
- hyperfy_data:/app/world
volumes:
hyperfy_data:
EOF

# Create Docker Compose .env file
cat > "${ENV_FILE}" <<EOF
HOST_REPO_DIR=${CONFIG_DIR}
HYPERFY_PORT=${HYPERFY_PORT}
HYPERFY_CONTAINER_NAME=${CONTAINER_NAME}
EOF

# Create Hyperfy application .env file
cat > "${CONFIG_DIR}/.env" <<EOF
# The world folder to run
WORLD=${WORLD_NAME}

# The port the server runs on
PORT=${HYPERFY_PORT}

# The secret the server uses to create/parse json web tokens
JWT_SECRET=hyper

# The code used to become admin in the world (type /admin <code> in chat)
# If left blank, everyone is an admin!
ADMIN_CODE=${ADMIN_CODE}

# How often the world saves (seconds)
# Can be set to 0 to disable saving
SAVE_INTERVAL=${SAVE_INTERVAL}

# Whether players should collide with other players
PUBLIC_PLAYER_COLLISION=${PLAYER_COLLISION}

# The maximum upload file size for models etc (mb)
PUBLIC_MAX_UPLOAD_SIZE=${MAX_UPLOAD_SIZE}

# The public web socket url the client connects to
PUBLIC_WS_URL=ws://localhost:${HYPERFY_PORT}/ws

# The public url used by clients to access api (eg upload assets)
PUBLIC_API_URL=http://localhost:${HYPERFY_PORT}/api

# How assets are stored, fetched and uploaded (local or s3)
ASSETS=${ASSETS_TYPE}
ASSETS_BASE_URL=http://localhost:${HYPERFY_PORT}/assets
ASSETS_S3_URI=${ASSETS_S3_URI}

# By default world data is stored in a local sqlite database in the world folder
# Optionally set this to a postgres uri to store remotely, eg 'postgres://username:password@host:port/database'
DB_URI=${DB_URI}
DB_SCHEMA=${DB_SCHEMA}

# Whether the server should do a cleanup of unused blueprints and assets before launching
CLEAN=${CLEAN_ON_START}

# LiveKit (voice chat)
LIVEKIT_WS_URL=${LIVEKIT_WS_URL}
LIVEKIT_API_KEY=${LIVEKIT_API_KEY}
LIVEKIT_API_SECRET=${LIVEKIT_API_SECRET}

##
# AI
# --
# AI_PROVIDER: openai, anthropic, xai, google
# AI_MODEL: claude-opus-4-1-20250805, claude-sonnet-4-20250514, gpt-5, gpt-5-mini, gpt-5-nano, grok-4-0709, gemini-2.5-pro, gemini-2.5-flash
# AI_EFFORT: minimal, low, medium, high (OpenAI only)
# AI_API_KEY: The api key for the selected provider
##
AI_PROVIDER=${AI_PROVIDER}
AI_MODEL=${AI_MODEL}
AI_EFFORT=${AI_EFFORT}
AI_API_KEY=${AI_API_KEY}
EOF

# Create webapp launcher
APP_DISPLAY_NAME="${APP_NAME}"
echo
info "Creating webapp launcher..."

# Create Hyperfy icon
ICON_DIR="$HOME/.local/share/applications/icons"
mkdir -p "${ICON_DIR}"

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
HYPERFY_ICON="${SCRIPT_DIR}/hyperfy.png"

if [ -f "${HYPERFY_ICON}" ]; then
info "Using provided Hyperfy icon"
cp -f "${HYPERFY_ICON}" "${ICON_DIR}/Hyperfy.png"
else
info "Hyperfy icon not found at ${HYPERFY_ICON}, creating default icon"
convert -size 64x64 xc:transparent -fill "#FF6B35" -draw "polygon 32,8 48,24 32,40 16,24" "${ICON_DIR}/Hyperfy.png" 2>/dev/null || {
echo "Creating fallback icon..."
touch "${ICON_DIR}/Hyperfy.png"
}
fi

# Create desktop webapp entry
if command -v omarchy-webapp-install >/dev/null 2>&1; then
ICON_FILENAME="${APP_NAME}.png"
gum spin --title "Installing as webapp..." -- \
omarchy-webapp-install "${APP_DISPLAY_NAME}" "http://localhost:${HYPERFY_PORT}" "${ICON_FILENAME}" "omarchy-launch-hyperfy"
else
echo "omarchy-webapp-install not found, creating basic desktop entry"
DESKTOP_FILE="$HOME/.local/share/applications/${APP_DISPLAY_NAME}.desktop"
cat >"$DESKTOP_FILE" <<EOF
[Desktop Entry]
Version=1.0
Name=${APP_DISPLAY_NAME}
Comment=${APP_DISPLAY_NAME}
Exec=omarchy-launch-hyperfy
Terminal=false
Type=Application
Icon=${ICON_DIR}/Hyperfy.png
StartupNotify=true
EOF
chmod +x "$DESKTOP_FILE"
fi

# Start container and health-check
gum spin --title "Starting container..." -- \
docker compose -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" up -d

APP_URL="http://127.0.0.1:${HYPERFY_PORT}"
if wait_http_ready "${APP_URL}" 45; then
info "Service is reachable at ${APP_URL}"
else
info "Service not reachable yet. It may still be building dependencies. You can check logs:"
echo " docker compose -f '${COMPOSE_FILE}' --env-file '${ENV_FILE}' logs -f"
fi

# Launch Hyperfy in background so installer can continue
echo
header "Installation Complete"
info "Launching Hyperfy..."
omarchy-launch-hyperfy &
sleep 2

# Note: Uninstall is now handled through the webapp removal system
echo "To uninstall Hyperfy, use: omarchy-webapp-remove"
echo
info "Configuration files location:"
echo " • Docker Compose: ${COMPOSE_FILE}"
echo " • Environment: ${CONFIG_DIR}/.env"
echo " • Data directory: ${APP_ROOT}"
echo
echo "You can edit these files to customize your Hyperfy installation."
27 changes: 27 additions & 0 deletions bin/omarchy-launch-hyperfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -Eeuo pipefail

APP_SLUG="hyperfy"
STACK_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/omarchy/games/${APP_SLUG}/compose"
COMPOSE_FILE="${STACK_DIR}/docker-compose.yml"
ENV_FILE="${STACK_DIR}/.env"

[ -f "$ENV_FILE" ] || { echo "Not installed. Run: omarchy-install-hyperfy"; exit 1; }
# shellcheck disable=SC1090
source "$ENV_FILE"

docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d

URL="http://127.0.0.1:${HYPERFY_PORT}"
# Wait for readiness
for i in {1..20}; do
if curl -fsS "$URL" >/dev/null 2>&1; then break; fi
sleep 1
done

# Launch as webapp using omarchy-launch-webapp if available
if command -v omarchy-launch-webapp >/dev/null 2>&1; then
omarchy-launch-webapp "$URL"
else
xdg-open "$URL"
fi
Loading