feat: unified HomeAI dashboard — merge character + desktop into single app
Combines homeai-character (service status, character profiles, editor) and homeai-desktop (chat with voice I/O) into homeai-dashboard on port 5173. - 4-page sidebar layout: Dashboard, Chat, Characters, Editor - Merged Vite middleware: health checks, service restart, bridge proxy - Bridge upgraded to ThreadingHTTPServer (fixes LAN request queuing) - TTS strips emojis before synthesis - Updated start.sh with new launchd service names - Added preload-models to startup sequence Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
221
start.sh
Executable file
221
start.sh
Executable file
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env bash
|
||||
# start.sh — Start all HomeAI services on LINDBLUM
|
||||
#
|
||||
# Usage:
|
||||
# ./start.sh # start everything
|
||||
# ./start.sh status # show what's running
|
||||
# ./start.sh stop # stop everything
|
||||
# ./start.sh restart # stop then start everything
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${REPO_DIR}/scripts/common.sh"
|
||||
|
||||
DOCKER_DIR="${HOME}/server/docker"
|
||||
|
||||
# ─── Service definitions ──────────────────────────────────────────────────────
|
||||
|
||||
LAUNCHD_SERVICES=(
|
||||
com.homeai.ollama
|
||||
com.homeai.wyoming-stt
|
||||
com.homeai.wyoming-tts
|
||||
com.homeai.wyoming-satellite
|
||||
com.homeai.openclaw
|
||||
com.homeai.openclaw-bridge
|
||||
com.homeai.preload-models
|
||||
com.homeai.dashboard
|
||||
)
|
||||
|
||||
DOCKER_COMPOSE_DIRS=(
|
||||
"${DOCKER_DIR}/open-webui"
|
||||
"${DOCKER_DIR}/uptime-kuma"
|
||||
"${DOCKER_DIR}/n8n"
|
||||
"${DOCKER_DIR}/code-server"
|
||||
)
|
||||
|
||||
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
launchd_running() {
|
||||
local label="$1"
|
||||
local pid
|
||||
pid=$(launchctl list "$label" 2>/dev/null | grep '"PID"' | sed 's/[^0-9]//g' || true)
|
||||
[[ -n "$pid" && "$pid" -gt 0 ]] 2>/dev/null
|
||||
}
|
||||
|
||||
start_launchd() {
|
||||
local label="$1"
|
||||
local plist="${HOME}/Library/LaunchAgents/${label}.plist"
|
||||
|
||||
if [[ ! -f "$plist" ]]; then
|
||||
log_warn "Plist not found: $plist — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
if launchd_running "$label"; then
|
||||
log_info "Already running: $label"
|
||||
return
|
||||
fi
|
||||
|
||||
log_step "Starting $label"
|
||||
# bootstrap loads + starts the agent
|
||||
launchctl bootstrap "gui/$(id -u)" "$plist" 2>/dev/null \
|
||||
|| launchctl kickstart -k "gui/$(id -u)/$label" 2>/dev/null \
|
||||
|| true
|
||||
sleep 0.5
|
||||
|
||||
if launchd_running "$label"; then
|
||||
log_success "$label started"
|
||||
else
|
||||
log_warn "$label may not have started — check logs"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_launchd() {
|
||||
local label="$1"
|
||||
local plist="${HOME}/Library/LaunchAgents/${label}.plist"
|
||||
|
||||
if [[ ! -f "$plist" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if ! launchd_running "$label"; then
|
||||
log_info "Already stopped: $label"
|
||||
return
|
||||
fi
|
||||
|
||||
log_step "Stopping $label"
|
||||
launchctl bootout "gui/$(id -u)/$label" 2>/dev/null || true
|
||||
log_success "$label stopped"
|
||||
}
|
||||
|
||||
start_docker_services() {
|
||||
ensure_docker_running
|
||||
|
||||
for dir in "${DOCKER_COMPOSE_DIRS[@]}"; do
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
log_warn "Docker compose dir not found: $dir — skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
local name
|
||||
name=$(basename "$dir")
|
||||
log_step "Starting Docker stack: $name"
|
||||
docker_compose -f "${dir}/docker-compose.yml" up -d 2>/dev/null \
|
||||
|| docker_compose -f "${dir}/compose.yml" up -d 2>/dev/null \
|
||||
|| log_warn "No compose file found in $dir"
|
||||
done
|
||||
}
|
||||
|
||||
stop_docker_services() {
|
||||
for dir in "${DOCKER_COMPOSE_DIRS[@]}"; do
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local name
|
||||
name=$(basename "$dir")
|
||||
log_step "Stopping Docker stack: $name"
|
||||
docker_compose -f "${dir}/docker-compose.yml" down 2>/dev/null \
|
||||
|| docker_compose -f "${dir}/compose.yml" down 2>/dev/null \
|
||||
|| true
|
||||
done
|
||||
}
|
||||
|
||||
# ─── Commands ─────────────────────────────────────────────────────────────────
|
||||
|
||||
do_start() {
|
||||
log_section "Starting HomeAI Services"
|
||||
|
||||
echo ""
|
||||
log_info "── LaunchD services ──"
|
||||
for svc in "${LAUNCHD_SERVICES[@]}"; do
|
||||
start_launchd "$svc"
|
||||
done
|
||||
|
||||
echo ""
|
||||
log_info "── Docker services ──"
|
||||
start_docker_services
|
||||
|
||||
echo ""
|
||||
log_success "All services started."
|
||||
echo ""
|
||||
do_status
|
||||
}
|
||||
|
||||
do_stop() {
|
||||
log_section "Stopping HomeAI Services"
|
||||
|
||||
echo ""
|
||||
log_info "── LaunchD services ──"
|
||||
for svc in "${LAUNCHD_SERVICES[@]}"; do
|
||||
stop_launchd "$svc"
|
||||
done
|
||||
|
||||
echo ""
|
||||
log_info "── Docker services ──"
|
||||
stop_docker_services
|
||||
|
||||
echo ""
|
||||
log_success "All services stopped."
|
||||
}
|
||||
|
||||
do_status() {
|
||||
log_section "HomeAI Service Status"
|
||||
|
||||
# LaunchD services
|
||||
printf "\n ${BOLD}%-34s %-10s %s${RESET}\n" "SERVICE" "STATUS" "PID"
|
||||
printf " %-34s %-10s %s\n" "─────────────────────────────────" "────────" "─────"
|
||||
|
||||
for svc in "${LAUNCHD_SERVICES[@]}"; do
|
||||
local pid
|
||||
pid=$(launchctl list "$svc" 2>/dev/null | grep '"PID"' | sed 's/[^0-9]//g' || true)
|
||||
if [[ -n "$pid" && "$pid" -gt 0 ]] 2>/dev/null; then
|
||||
printf " ${GREEN}●${RESET} %-32s ${GREEN}%-10s${RESET} %s\n" "$svc" "running" "$pid"
|
||||
else
|
||||
printf " ${RED}○${RESET} %-32s ${RED}%-10s${RESET} %s\n" "$svc" "stopped" "-"
|
||||
fi
|
||||
done
|
||||
|
||||
# Docker services
|
||||
echo ""
|
||||
local containers=("homeai-open-webui" "homeai-uptime-kuma" "homeai-n8n" "homeai-code-server")
|
||||
for c in "${containers[@]}"; do
|
||||
local state
|
||||
state=$(docker inspect -f '{{.State.Status}}' "$c" 2>/dev/null || echo "not found")
|
||||
if [[ "$state" == "running" ]]; then
|
||||
printf " ${GREEN}●${RESET} %-32s ${GREEN}%-10s${RESET}\n" "$c" "$state"
|
||||
else
|
||||
printf " ${RED}○${RESET} %-32s ${RED}%-10s${RESET}\n" "$c" "$state"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ─── Main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
main() {
|
||||
local cmd="${1:-start}"
|
||||
|
||||
case "$cmd" in
|
||||
start) do_start ;;
|
||||
stop) do_stop ;;
|
||||
restart) do_stop; echo ""; do_start ;;
|
||||
status) do_status ;;
|
||||
-h|--help|help)
|
||||
echo ""
|
||||
echo " Usage: $0 [start|stop|restart|status]"
|
||||
echo ""
|
||||
echo " Commands:"
|
||||
echo " start Start all HomeAI services (default)"
|
||||
echo " stop Stop all HomeAI services"
|
||||
echo " restart Stop then start all services"
|
||||
echo " status Show current service status"
|
||||
echo ""
|
||||
;;
|
||||
*) die "Unknown command: $cmd. Run '$0 help' for usage." ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user