#!/usr/bin/env bash # AgentTeams website Dashboard installer: v1.3.1 # Source: https://github.com/agentteams-group/agentteams-dashboard/blob/693088d7bb8e27929a26534be3740c4b2503e8b1/install/agentteams-dashboard.sh # Modified: support interactive pipe installs; pin the release, bind locally by default, and stop on image pull failure. # License: https://agentteams.cloud/agentteams-dashboard.LICENSE # ============================================================ # agentteams-dashboard.sh — Install / upgrade / uninstall AgentTeams-Dashboard # as an optional component of an existing AgentTeams deployment. # # Usage: # bash agentteams-dashboard.sh # interactive install (first time) # bash agentteams-dashboard.sh update # refresh configured image & recreate # bash agentteams-dashboard.sh uninstall # remove dashboard container # # Non-interactive: # AGENTTEAMS_NON_INTERACTIVE=1 AGENTTEAMS_PORT_DASHBOARD=13000 bash agentteams-dashboard.sh # ============================================================ website_main() { set -euo pipefail # ---------- constants ---------- CONTAINER_NAME="agentteams-dashboard" NETWORK_NAME="agentteams-net" DEFAULT_PORT=13000 DEFAULT_DASHBOARD_VERSION="v1.3.1" # Must match the Makefile image coordinates (REGISTRY/REPO/name). DEFAULT_REGISTRY="higress-registry.cn-hangzhou.cr.aliyuncs.com" DEFAULT_IMAGE="${DEFAULT_REGISTRY}/agentteams/agentteams-dashboard:${DEFAULT_DASHBOARD_VERSION}" DATA_VOLUME="agentteams-dashboard-data" DATA_MOUNT="/data/agentteams-dashboard" CONFIG_FILE="${DATA_MOUNT}/config.json" ENV_FILE="${HOME}/.agentteams-dashboard.env" # ---------- helpers ---------- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${CYAN}[INFO]${NC} $*"; } ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } detect_docker() { DOCKER_CMD="docker" if ! docker version >/dev/null 2>&1; then if podman version >/dev/null 2>&1; then DOCKER_CMD="podman" else err "Neither docker nor podman found. Please install one."; exit 1 fi fi info "Using: ${DOCKER_CMD}" } # ---------- env persistence ---------- save_env() { # Write to temp file first, then atomically rename, and enforce owner-only permissions. local _tmp _tmp="$(mktemp "${ENV_FILE}.XXXXXX")" cat > "${_tmp}" </dev/null 2>&1 || true ok "Dashboard container removed." else warn "Container ${CONTAINER_NAME} not found — nothing to do." fi } # ---------- pre-flight checks ---------- preflight() { detect_docker # 1. Check agentteams-net exists if ! ${DOCKER_CMD} network inspect "${NETWORK_NAME}" >/dev/null 2>&1; then err "Docker network '${NETWORK_NAME}' not found." err "Please install AgentTeams first: https://github.com/agentscope-ai/AgentTeams" exit 1 fi ok "Network '${NETWORK_NAME}' found." # 2. Check controller is running if ! ${DOCKER_CMD} ps --format '{{.Names}}' | grep -q "agentteams-controller\|agentteams-manager"; then warn "No running agentteams-controller or agentteams-manager container found." warn "Dashboard will start but may not be able to reach the controller." else ok "AgentTeams controller/manager container detected." fi } # ---------- prompts ---------- prompt_value() { local var_name="$1" prompt_text="$2" default="$3" if [ "${AGENTTEAMS_NON_INTERACTIVE:-0}" = "1" ]; then eval "${var_name}=\${${var_name}:-${default}}" return fi local current="${!var_name:-${default}}" read -rp "${prompt_text} [${current}]: " input eval "${var_name}=\${input:-${current}}" } prompt_yes_no() { local var_name="$1" prompt_text="$2" default="$3" if [ "${AGENTTEAMS_NON_INTERACTIVE:-0}" = "1" ]; then eval "${var_name}=\${${var_name}:-${default}}" return fi local hint="Y/n" current="${default}" [ "${default}" = "0" ] && hint="y/N" read -rp "${prompt_text} [${hint}]: " input case "${input:-}" in [yY1]) eval "${var_name}=1" ;; [nN0]) eval "${var_name}=0" ;; *) eval "${var_name}=${current}" ;; esac } # ---------- wizard ---------- wizard() { echo "" echo -e "${CYAN}========================================${NC}" echo -e "${CYAN} AgentTeams-Dashboard Installation Wizard${NC}" echo -e "${CYAN}========================================${NC}" echo "" prompt_yes_no AGENTTEAMS_DASHBOARD "Install AgentTeams-Dashboard?" "1" if [ "${AGENTTEAMS_DASHBOARD}" != "1" ]; then info "Dashboard installation skipped." exit 0 fi AGENTTEAMS_DASHBOARD_VERSION="${AGENTTEAMS_DASHBOARD_VERSION:-${DEFAULT_DASHBOARD_VERSION}}" prompt_value AGENTTEAMS_DASHBOARD_VERSION "Dashboard version" "${DEFAULT_DASHBOARD_VERSION}" # Derive default image from version if not explicitly set if [ -z "${AGENTTEAMS_DASHBOARD_IMAGE:-}" ]; then AGENTTEAMS_DASHBOARD_IMAGE="${DEFAULT_REGISTRY}/agentteams/agentteams-dashboard:${AGENTTEAMS_DASHBOARD_VERSION}" fi prompt_value AGENTTEAMS_PORT_DASHBOARD "Dashboard port" "${DEFAULT_PORT}" prompt_value AGENTTEAMS_DASHBOARD_IMAGE "Dashboard Docker image" "${AGENTTEAMS_DASHBOARD_IMAGE}" # Detect controller URL — in embedded mode the API is on agentteams-controller:8090. # The manager container does not expose the controller REST API, so we always # prefer agentteams-controller and only fall back to agentteams-manager as a last resort. local ctrl_url="" if ${DOCKER_CMD} ps --format '{{.Names}}' | grep -q "^agentteams-controller$"; then if ${DOCKER_CMD} exec agentteams-controller wget -q -O- --timeout=2 http://127.0.0.1:8090/healthz >/dev/null 2>&1; then ctrl_url="http://agentteams-controller:8090" fi fi if [ -z "${ctrl_url}" ] && ${DOCKER_CMD} ps --format '{{.Names}}' | grep -q "^agentteams-manager$"; then warn "agentteams-controller not available — falling back to agentteams-manager:8090" if ${DOCKER_CMD} exec agentteams-manager wget -q -O- --timeout=2 http://127.0.0.1:8090/healthz >/dev/null 2>&1; then ctrl_url="http://agentteams-manager:8090" fi fi [ -z "${ctrl_url}" ] && ctrl_url="http://agentteams-controller:8090" prompt_value AGENTTEAMS_CONTROLLER_URL "AgentTeams Controller URL" "${ctrl_url}" prompt_value NEXT_PUBLIC_MATRIX_API_URL "Matrix Homeserver URL" "http://agentteams-controller:6167" # Multi-user session secret (M19): generated once and persisted so container # rebuilds keep the same secret (a changed secret invalidates all sessions). if [ -z "${DASHBOARD_SESSION_SECRET:-}" ]; then DASHBOARD_SESSION_SECRET="$(openssl rand -hex 32)" fi info "DASHBOARD_SESSION_SECRET ready (persisted in ${ENV_FILE}, mode 600)." # Detect Higress Console URL from running container (for shared auth with Higress) # Use internal Docker network URL (container:port) — works for dashboard on the same network. local higress_url="" if [ "${AGENTTEAMS_HIGRESS_ADAPTER_MODE:-direct}" != "external" ]; then for _ctr in "agentteams-controller" "higress-console" "higress"; do if ${DOCKER_CMD} ps --format '{{.Names}}' | grep -q "^${_ctr}$"; then # Prefer internal port 8001 (Higress Console API) if ${DOCKER_CMD} exec "${_ctr}" wget -q -O- --timeout=2 http://127.0.0.1:8001/ >/dev/null 2>&1; then higress_url="http://${_ctr}:8001" break fi fi done fi prompt_value AGENTTEAMS_AI_GATEWAY_ADMIN_URL "Higress Console URL (for shared login)" "${higress_url}" # Embedded deployment marker: when the controller container lives on the # agentteams-net alongside the dashboard, set the deployment mode so the # process picks embedded defaults on first boot (F-1 / F-3). Only set when # the operator hasn't overridden it via AGENTTEAMS_DEPLOYMENT_MODE. if [ -z "${AGENTTEAMS_DEPLOYMENT_MODE:-}" ]; then if ${DOCKER_CMD} ps --format '{{.Names}}' | grep -q "^agentteams-controller$"; then if ${DOCKER_CMD} network inspect "${NETWORK_NAME}" >/dev/null 2>&1 \ && ${DOCKER_CMD} exec agentteams-controller wget -q -O- --timeout=2 http://127.0.0.1:8090/healthz >/dev/null 2>&1; then AGENTTEAMS_DEPLOYMENT_MODE="embedded" fi fi [ -z "${AGENTTEAMS_DEPLOYMENT_MODE:-}" ] && AGENTTEAMS_DEPLOYMENT_MODE="external" fi info "AGENTTEAMS_DEPLOYMENT_MODE=${AGENTTEAMS_DEPLOYMENT_MODE}" } # ---------- local-only binding ---------- resolve_port_prefix() { _port_prefix="" if [ "${AGENTTEAMS_LOCAL_ONLY:-1}" = "1" ]; then _port_prefix="127.0.0.1:" fi } # ---------- ensure released image ---------- ensure_image() { local image="$1" if ! ${DOCKER_CMD} image inspect "${image}" >/dev/null 2>&1; then info "Pulling image ${image}..." ${DOCKER_CMD} pull "${image}" || { err "Cannot pull ${image}. Installation stopped; no local source build was attempted." exit 1 } fi } # ---------- runtime env detection from controller container ---------- detect_runtime_env() { # MinIO / LLM / auth credentials live on the controller container, even when the # dashboard talks to a manager or proxy. Prefer agentteams-controller, fall back to # the host derived from AGENTTEAMS_CONTROLLER_URL. if ${DOCKER_CMD} ps --format '{{.Names}}' | grep -qx "agentteams-controller"; then if echo "${AGENTTEAMS_CONTROLLER_URL}" | grep -q "agentteams-manager"; then warn "Controller URL points to agentteams-manager; switching to agentteams-controller:8090 for API access" AGENTTEAMS_CONTROLLER_URL="http://agentteams-controller:8090" fi fi local ctrl_container="" if ${DOCKER_CMD} ps --format '{{.Names}}' | grep -qx "agentteams-controller"; then ctrl_container="agentteams-controller" else ctrl_container=$(echo "${AGENTTEAMS_CONTROLLER_URL}" | sed -n 's|^http://\([^/:]*\).*|\1|p') fi if [ -z "${ctrl_container}" ]; then warn "Could not derive controller container name from ${AGENTTEAMS_CONTROLLER_URL}" return fi if ! ${DOCKER_CMD} ps --format '{{.Names}}' | grep -qx "${ctrl_container}"; then warn "Controller container '${ctrl_container}' not running — cannot auto-detect MinIO/LLM credentials" return fi info "Detecting runtime environment from ${ctrl_container}..." local env_out env_out=$(${DOCKER_CMD} inspect "${ctrl_container}" --format='{{range .Config.Env}}{{.}}{{"\n"}}{{end}}') # Credential detection chain (A7/B-C1): AGENTTEAMS_FS_* is the canonical # namespace, AGENTTEAMS_MINIO_* the long-standing alias, RUSTFS_* the # fallback for controllers that switched storage backends without keeping # the older exports (see tasklist B-C1 / AGENTTEAMS_PATCH.md). AGENTTEAMS_FS_BUCKET=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_FS_BUCKET=//p') [ -z "${AGENTTEAMS_FS_BUCKET}" ] && AGENTTEAMS_FS_BUCKET=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_MINIO_BUCKET=//p') [ -z "${AGENTTEAMS_FS_BUCKET}" ] && AGENTTEAMS_FS_BUCKET=$(echo "${env_out}" | sed -n 's/^RUSTFS_BUCKET=//p') AGENTTEAMS_FS_ACCESS_KEY=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_FS_ACCESS_KEY=//p') [ -z "${AGENTTEAMS_FS_ACCESS_KEY}" ] && AGENTTEAMS_FS_ACCESS_KEY=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_MINIO_USER=//p') [ -z "${AGENTTEAMS_FS_ACCESS_KEY}" ] && AGENTTEAMS_FS_ACCESS_KEY=$(echo "${env_out}" | sed -n 's/^RUSTFS_ACCESS_KEY=//p') AGENTTEAMS_FS_SECRET_KEY=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_FS_SECRET_KEY=//p') [ -z "${AGENTTEAMS_FS_SECRET_KEY}" ] && AGENTTEAMS_FS_SECRET_KEY=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_MINIO_PASSWORD=//p') [ -z "${AGENTTEAMS_FS_SECRET_KEY}" ] && AGENTTEAMS_FS_SECRET_KEY=$(echo "${env_out}" | sed -n 's/^RUSTFS_SECRET_KEY=//p') local fs_endpoint fs_endpoint=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_FS_ENDPOINT=//p') [ -z "${fs_endpoint}" ] && fs_endpoint=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_MINIO_ENDPOINT=//p') [ -z "${fs_endpoint}" ] && fs_endpoint=$(echo "${env_out}" | sed -n 's/^RUSTFS_ENDPOINT=//p') if [ -n "${fs_endpoint}" ]; then # Controller often advertises MinIO as 127.0.0.1, but Dashboard runs in a different container. AGENTTEAMS_FS_ENDPOINT=$(echo "${fs_endpoint}" | sed -e "s|127\\.0\\.0\\.1|${ctrl_container}|" -e "s|localhost|${ctrl_container}|") else AGENTTEAMS_FS_ENDPOINT="http://${ctrl_container}:9000" fi AGENTTEAMS_LLM_PROVIDER=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_LLM_PROVIDER=//p') AGENTTEAMS_LLM_API_KEY=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_LLM_API_KEY=//p') AGENTTEAMS_OPENAI_BASE_URL=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_OPENAI_BASE_URL=//p') AGENTTEAMS_DEFAULT_MODEL=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_DEFAULT_MODEL=//p') # The controller writes the CLI SA token to /var/run/agentteams/cli-token. # The token is generated asynchronously during the first reconcile loop, # so it may not be available immediately after the container starts. # We poll for up to 30s to handle both fresh installs (token not yet minted) # and older images that don't support cli-token at all. # For backward compatibility, also try the legacy /var/run/hiclaw/cli-token # path used by older HiClaw images. local _token_wait=0 local _token_max_wait=30 while [ ${_token_wait} -lt ${_token_max_wait} ]; do AGENTTEAMS_AUTH_TOKEN=$(${DOCKER_CMD} exec "${ctrl_container}" sh -c 'cat /var/run/agentteams/cli-token 2>/dev/null || cat /var/run/hiclaw/cli-token 2>/dev/null' | tr -d '\n' || true) if [ -n "${AGENTTEAMS_AUTH_TOKEN}" ]; then break fi sleep 2 _token_wait=$((_token_wait + 2)) done # Admin credentials are set on the controller container by the AgentTeams # installer; the dashboard needs them for the Higress ensure-ai bootstrap. AGENTTEAMS_ADMIN_USER=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_ADMIN_USER=//p') AGENTTEAMS_ADMIN_PASSWORD=$(echo "${env_out}" | sed -n 's/^AGENTTEAMS_ADMIN_PASSWORD=//p') # Higress Console URL: explicit config takes priority, auto-detect as fallback. if [ "${AGENTTEAMS_HIGRESS_ADAPTER_MODE:-direct}" != "external" ] && [ -z "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL:-}" ]; then if ${DOCKER_CMD} exec "${ctrl_container}" wget -q -O- --timeout=2 http://127.0.0.1:8001/ >/dev/null 2>&1; then AGENTTEAMS_AI_GATEWAY_ADMIN_URL="http://${ctrl_container}:8001" fi elif [ -n "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL:-}" ]; then # Normalize URL: add http:// prefix if missing case "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}" in http://*|https://*) ;; *) AGENTTEAMS_AI_GATEWAY_ADMIN_URL="http://${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}" ;; esac fi if [ -z "${AGENTTEAMS_AUTH_TOKEN}" ]; then warn "Could not read controller auth token (cli-token) from ${ctrl_container} (timed out after ${_token_max_wait}s)." warn " This can happen with older AgentTeams/HiClaw images or if the controller is still starting." warn " Dashboard will still work but some API calls may fail until you log in via Higress Console." warn " To fix: upgrade AgentTeams to v1.2.0-beta.1+ or set AGENTTEAMS_AUTH_TOKEN manually." fi if [ -z "${AGENTTEAMS_FS_ACCESS_KEY}" ] || [ -z "${AGENTTEAMS_FS_SECRET_KEY}" ]; then warn "Could not auto-detect storage credentials from ${ctrl_container}" warn " Detection chain tried: AGENTTEAMS_FS_* -> AGENTTEAMS_MINIO_* -> RUSTFS_*" warn " Set AGENTTEAMS_FS_ENDPOINT / AGENTTEAMS_FS_ACCESS_KEY / AGENTTEAMS_FS_SECRET_KEY manually to fix." fi if [ -z "${AGENTTEAMS_LLM_API_KEY}" ]; then warn "Could not auto-detect LLM API key from ${ctrl_container}" fi # Verify Higress Console URL reachability (best-effort warning) if [ -n "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL:-}" ]; then local _gw_reachable=0 if echo "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}" | grep -qE "(agentteams-controller|hiclaw-controller|127\.0\.0\.1|localhost)"; then if ${DOCKER_CMD} exec "${ctrl_container}" curl -sf --max-time 3 "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}/" >/dev/null 2>&1; then _gw_reachable=1 fi else if curl -sf --max-time 3 "${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}/" >/dev/null 2>&1; then _gw_reachable=1 fi fi if [ "${_gw_reachable}" = "0" ]; then warn "Higress Console URL may not be reachable: ${AGENTTEAMS_AI_GATEWAY_ADMIN_URL}" warn " Dashboard will still work, but shared login via Higress Console may fail." warn " To fix: verify the URL is correct and the Higress Console service is running." fi fi } # ---------- recreate container ---------- recreate_container() { resolve_port_prefix # Remove existing container if present if ${DOCKER_CMD} ps -a --format '{{.Names}}' | grep -qx "${CONTAINER_NAME}"; then info "Removing existing ${CONTAINER_NAME} container..." ${DOCKER_CMD} rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true fi # Auto-detect MinIO / LLM / auth from the controller container detect_runtime_env # Build environment argument list local env_args=() env_args+=(-e AGENTTEAMS_CONTROLLER_URL="${AGENTTEAMS_CONTROLLER_URL}") env_args+=(-e NEXT_PUBLIC_MATRIX_API_URL="${NEXT_PUBLIC_MATRIX_API_URL}") env_args+=(-e AGENTTEAMS_HIGRESS_ADAPTER_MODE="${AGENTTEAMS_HIGRESS_ADAPTER_MODE:-direct}") env_args+=(-e AGENTTEAMS_AI_GATEWAY_URL="${AGENTTEAMS_AI_GATEWAY_URL:-}") env_args+=(-e AGENTTEAMS_AI_GATEWAY_ADMIN_URL="${AGENTTEAMS_AI_GATEWAY_ADMIN_URL:-}") env_args+=(-e AGENTTEAMS_AI_GATEWAY_ADMIN_ALLOWED_HOSTS="${AGENTTEAMS_AI_GATEWAY_ADMIN_ALLOWED_HOSTS:-}") env_args+=(-e MATRIX_HOMESERVER_ALLOWLIST="agentteams-controller,matrix-local.agentteams.io,matrix.org") [ -n "${AGENTTEAMS_AUTH_TOKEN:-}" ] && env_args+=(-e AGENTTEAMS_AUTH_TOKEN="${AGENTTEAMS_AUTH_TOKEN}") env_args+=(-e DASHBOARD_SESSION_SECRET="${DASHBOARD_SESSION_SECRET:-}") env_args+=(-e DASHBOARD_CONFIG_FILE="${DASHBOARD_CONFIG_FILE:-${CONFIG_FILE}}") env_args+=(-e AGENTTEAMS_DEPLOYMENT_MODE="${AGENTTEAMS_DEPLOYMENT_MODE:-external}") [ -n "${AGENTTEAMS_ADMIN_USER:-}" ] && env_args+=(-e AGENTTEAMS_ADMIN_USER="${AGENTTEAMS_ADMIN_USER}") [ -n "${AGENTTEAMS_ADMIN_PASSWORD:-}" ] && env_args+=(-e AGENTTEAMS_ADMIN_PASSWORD="${AGENTTEAMS_ADMIN_PASSWORD}") [ -n "${AGENTTEAMS_FS_ENDPOINT:-}" ] && env_args+=(-e AGENTTEAMS_FS_ENDPOINT="${AGENTTEAMS_FS_ENDPOINT}") [ -n "${AGENTTEAMS_FS_ACCESS_KEY:-}" ] && env_args+=(-e AGENTTEAMS_FS_ACCESS_KEY="${AGENTTEAMS_FS_ACCESS_KEY}") [ -n "${AGENTTEAMS_FS_SECRET_KEY:-}" ] && env_args+=(-e AGENTTEAMS_FS_SECRET_KEY="${AGENTTEAMS_FS_SECRET_KEY}") [ -n "${AGENTTEAMS_FS_BUCKET:-}" ] && env_args+=(-e AGENTTEAMS_FS_BUCKET="${AGENTTEAMS_FS_BUCKET}") [ -n "${AGENTTEAMS_LLM_PROVIDER:-}" ] && env_args+=(-e AGENTTEAMS_LLM_PROVIDER="${AGENTTEAMS_LLM_PROVIDER}") [ -n "${AGENTTEAMS_LLM_API_KEY:-}" ] && env_args+=(-e AGENTTEAMS_LLM_API_KEY="${AGENTTEAMS_LLM_API_KEY}") [ -n "${AGENTTEAMS_OPENAI_BASE_URL:-}" ] && env_args+=(-e AGENTTEAMS_OPENAI_BASE_URL="${AGENTTEAMS_OPENAI_BASE_URL}") [ -n "${AGENTTEAMS_DEFAULT_MODEL:-}" ] && env_args+=(-e AGENTTEAMS_DEFAULT_MODEL="${AGENTTEAMS_DEFAULT_MODEL}") # Persist the latest detected values save_env # Start container info "Starting ${CONTAINER_NAME}..." ${DOCKER_CMD} volume create "${DATA_VOLUME}" >/dev/null 2>&1 || true # Migration: legacy installs wrote into /app/db inside the same volume. # Detect /app/db contents and bind that mount once so existing files survive # this cycle. Next upgrade drops the legacy bind. local _vol_args=() _vol_args+=(-v "${DATA_VOLUME}:${DATA_MOUNT}") local _legacy_entries _legacy_entries=$(${DOCKER_CMD} run --rm -v "${DATA_VOLUME}:/probe" alpine:latest sh -c 'ls -1A /probe/app 2>/dev/null' 2>/dev/null || true) if [ -n "${_legacy_entries}" ]; then warn "dashboard data volume has files under /app/db (legacy path); mounting it once so this upgrade does not lose them. Next upgrade will drop the legacy bind." _vol_args+=(-v "${DATA_VOLUME}:/app/db") fi ${DOCKER_CMD} run -d \ --name "${CONTAINER_NAME}" \ --restart unless-stopped \ --network "${NETWORK_NAME}" \ -p "${_port_prefix}${AGENTTEAMS_PORT_DASHBOARD}:3000" \ "${_vol_args[@]}" \ "${env_args[@]}" \ "${AGENTTEAMS_DASHBOARD_IMAGE}" # Wait for readiness info "Waiting for Dashboard to become ready..." local max_wait=60 elapsed=0 while [ ${elapsed} -lt ${max_wait} ]; do if curl -sf "http://127.0.0.1:${AGENTTEAMS_PORT_DASHBOARD}/" >/dev/null 2>&1; then break fi sleep 2 elapsed=$((elapsed + 2)) done if [ ${elapsed} -ge ${max_wait} ]; then warn "Dashboard did not respond within ${max_wait}s. Check logs: ${DOCKER_CMD} logs ${CONTAINER_NAME}" else ok "Dashboard is ready!" fi } # ---------- print success ---------- print_success() { echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} AgentTeams-Dashboard ${1:-installed} successfully!${NC}" echo -e "${GREEN}========================================${NC}" echo "" local bind_host="0.0.0.0" [ "${AGENTTEAMS_LOCAL_ONLY:-1}" = "1" ] && bind_host="127.0.0.1" echo -e " Access: ${CYAN}http://${bind_host}:${AGENTTEAMS_PORT_DASHBOARD}/${NC}" echo -e " Logs: ${DOCKER_CMD} logs -f ${CONTAINER_NAME}" echo -e " Stop: ${DOCKER_CMD} stop ${CONTAINER_NAME}" echo "" } # ---------- install (first time) ---------- do_install() { preflight wizard echo "" info "Configuration:" info " Version: ${AGENTTEAMS_DASHBOARD_VERSION}" info " Port: ${AGENTTEAMS_PORT_DASHBOARD}" info " Image: ${AGENTTEAMS_DASHBOARD_IMAGE}" info " Controller: ${AGENTTEAMS_CONTROLLER_URL}" info " Matrix: ${NEXT_PUBLIC_MATRIX_API_URL}" info " Higress: ${AGENTTEAMS_AI_GATEWAY_ADMIN_URL:-}" info " MinIO: ${AGENTTEAMS_FS_ENDPOINT:-} (bucket: ${AGENTTEAMS_FS_BUCKET:-})" info " LLM: ${AGENTTEAMS_LLM_PROVIDER:-} / ${AGENTTEAMS_DEFAULT_MODEL:-}" info " LAN access: $([ "${AGENTTEAMS_LOCAL_ONLY:-1}" = "1" ] && echo 'disabled (127.0.0.1 only)' || echo 'enabled (0.0.0.0)')" echo "" save_env ensure_image "${AGENTTEAMS_DASHBOARD_IMAGE}" recreate_container print_success "installed" } # ---------- update (upgrade existing) ---------- do_update() { preflight load_env echo "" info "Updating AgentTeams-Dashboard..." info " Port: ${AGENTTEAMS_PORT_DASHBOARD}" info " Image: ${AGENTTEAMS_DASHBOARD_IMAGE}" info " Controller: ${AGENTTEAMS_CONTROLLER_URL}" info " MinIO: ${AGENTTEAMS_FS_ENDPOINT:-} (bucket: ${AGENTTEAMS_FS_BUCKET:-})" info " LLM: ${AGENTTEAMS_LLM_PROVIDER:-} / ${AGENTTEAMS_DEFAULT_MODEL:-}" echo "" # Refresh the image selected in the saved configuration info "Pulling configured image ${AGENTTEAMS_DASHBOARD_IMAGE}..." ${DOCKER_CMD} pull "${AGENTTEAMS_DASHBOARD_IMAGE}" || { err "Cannot pull ${AGENTTEAMS_DASHBOARD_IMAGE}. Update stopped; existing container was preserved." exit 1 } recreate_container print_success "updated" } # ---------- main ---------- case "${1:-}" in uninstall|remove|rm) do_uninstall ;; update|upgrade) do_update ;; *) do_install ;; esac } # Connect interactive pipe installs to the terminal; preserve file and automation input. if [ -z "${BASH_SOURCE[0]:-}" ] && [ "${AGENTTEAMS_NON_INTERACTIVE:-0}" != "1" ]; then if ! { true /dev/null; then echo "Interactive installation requires a terminal. For automation, set AGENTTEAMS_NON_INTERACTIVE=1 and the required configuration." >&2 exit 1 fi website_main "$@"