#!/bin/bash set -euo pipefail PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="$PROJECT_DIR/.env" read_env_var() { local key="$1" if [ ! -f "$ENV_FILE" ]; then return 0 fi local line line=$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 || true) if [ -n "$line" ]; then printf '%s' "${line#*=}" fi } resolve_abs_path() { local raw_path="$1" if [[ "$raw_path" = /* ]]; then printf '%s' "$raw_path" return fi if command -v realpath >/dev/null 2>&1; then realpath -m "$PROJECT_DIR/$raw_path" return fi (cd "$PROJECT_DIR" && printf '%s/%s\n' "$PWD" "${raw_path#./}") } if [ -z "${GPU_TYPE:-}" ]; then GPU_TYPE="$(read_env_var GPU_TYPE)" fi if [ -z "${GPU_TYPE:-}" ]; then echo "ERROR: 'GPU_TYPE' is not defined!" echo "Please set it in your shell (export GPU_TYPE=amd) or create an .env file." exit 1 fi case "$GPU_TYPE" in amd|intel|nvidia) COMPOSE_FILE="$PROJECT_DIR/compose.$GPU_TYPE.yaml" ;; *) echo "ERROR: Invalid GPU_TYPE '$GPU_TYPE'!" echo "Allowed values are: amd, intel, nvidia" exit 1 ;; esac if [ ! -f "$COMPOSE_FILE" ]; then echo "ERROR: The file '$COMPOSE_FILE' was not found!" exit 1 fi if [ -z "${CONTAINER_ENGINE:-}" ]; then CONTAINER_ENGINE="$(read_env_var CONTAINER_ENGINE)" fi if [ -z "${CONTAINER_ENGINE:-}" ]; then if command -v podman-compose >/dev/null 2>&1; then CONTAINER_ENGINE="podman" elif command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then CONTAINER_ENGINE="docker" else echo "ERROR: Neither podman-compose nor docker compose is available." exit 1 fi fi case "$CONTAINER_ENGINE" in podman) COMPOSE_CMD=(podman-compose) ENGINE_CMD=(podman) ;; docker) COMPOSE_CMD=(docker compose) ENGINE_CMD=(docker) ;; *) echo "ERROR: Invalid CONTAINER_ENGINE '$CONTAINER_ENGINE'. Use 'podman' or 'docker'." exit 1 ;; esac if [ -z "${HOST_USER_ID:-}" ]; then HOST_USER_ID="$(read_env_var HOST_USER_ID)" fi if [ -z "${HOST_USER_ID:-}" ]; then HOST_USER_ID="$(id -u)" fi export HOST_USER_ID if [ -z "${HOST_WORKSPACE_PATH:-}" ]; then HOST_WORKSPACE_PATH="$(read_env_var HOST_WORKSPACE_PATH)" fi if [ -z "${HOST_WORKSPACE_PATH:-}" ]; then HOST_WORKSPACE_PATH="./storage/workspace" fi export HOST_WORKSPACE_PATH if [ -z "${HOST_REPOS_PATH:-}" ]; then HOST_REPOS_PATH="$(read_env_var HOST_REPOS_PATH)" fi if [ -z "${HOST_REPOS_PATH:-}" ]; then HOST_REPOS_PATH="$PROJECT_DIR" fi export HOST_REPOS_PATH HOST_WORKSPACE_ABS="$(resolve_abs_path "$HOST_WORKSPACE_PATH")" if [ "$CONTAINER_ENGINE" = "podman" ]; then : "${HOST_DOCKER_SOCKET_PATH:=/run/user/${HOST_USER_ID}/podman/podman.sock}" : "${HOST_DOCKER_SOCKET_MOUNT_OPTS:=:Z}" : "${HOST_OPENCLAW_CONFIG_MOUNT_OPTS:=:ro,Z}" : "${HOST_OPENCLAW_WORKSPACE_MOUNT_OPTS:=:Z}" : "${HOST_REPOS_MOUNT_OPTS:=:Z}" : "${HOST_RW_MOUNT_OPTS:=:Z,U}" : "${OPENCLAW_SANDBOX_UIDSHIFT:=1}" else : "${HOST_DOCKER_SOCKET_PATH:=/var/run/docker.sock}" : "${HOST_DOCKER_SOCKET_MOUNT_OPTS:=}" : "${HOST_OPENCLAW_CONFIG_MOUNT_OPTS:=:ro}" : "${HOST_OPENCLAW_WORKSPACE_MOUNT_OPTS:=}" : "${HOST_REPOS_MOUNT_OPTS:=}" : "${HOST_RW_MOUNT_OPTS:=}" : "${OPENCLAW_SANDBOX_UIDSHIFT:=0}" fi : "${OPENCLAW_SANDBOX_HOST_WORKSPACE:=$HOST_WORKSPACE_ABS}" : "${OPENCLAW_SANDBOX_CONTAINER_WORKSPACE:=/home/node/.openclaw/workspace}" export HOST_DOCKER_SOCKET_PATH export HOST_DOCKER_SOCKET_MOUNT_OPTS export HOST_OPENCLAW_CONFIG_MOUNT_OPTS export HOST_OPENCLAW_WORKSPACE_MOUNT_OPTS export HOST_REPOS_MOUNT_OPTS export HOST_RW_MOUNT_OPTS export OPENCLAW_SANDBOX_UIDSHIFT export OPENCLAW_SANDBOX_HOST_WORKSPACE export OPENCLAW_SANDBOX_CONTAINER_WORKSPACE compose() { "${COMPOSE_CMD[@]}" -f "$COMPOSE_FILE" "$@" } build_sandbox_image() { local sandbox_tag="localhost/ghostnet-openclaw_sandbox:latest" echo "Building sandbox base image ($sandbox_tag) with ${ENGINE_CMD[*]}..." "${ENGINE_CMD[@]}" build \ -t "$sandbox_tag" \ -f "$PROJECT_DIR/openclaw/sandbox/Containerfile" \ "$PROJECT_DIR/openclaw/sandbox" } COMMAND="${1:-}" cd "$PROJECT_DIR" case "$COMMAND" in up) echo "Starting Ghostnet ($GPU_TYPE) with $CONTAINER_ENGINE..." build_sandbox_image compose up -d ;; down) echo "Stopping Ghostnet ($GPU_TYPE) with $CONTAINER_ENGINE..." compose down ;; restart) echo "Restarting Ghostnet ($GPU_TYPE) with $CONTAINER_ENGINE..." compose restart ;; logs) compose logs -f ;; attached) echo "Checking for running instances..." compose down > /dev/null 2>&1 || true echo "Starting Ghostnet in foreground ($GPU_TYPE) with $CONTAINER_ENGINE..." build_sandbox_image compose up ;; *) echo "Usage: ./ghostnet.sh [up|down|restart|logs|attached]" echo "Current environment: $GPU_TYPE" echo "Container engine: $CONTAINER_ENGINE" exit 1 ;; esac