Files
zk-data-agent/scripts/start-webui.sh
T
2026-05-11 20:26:06 +08:00

183 lines
5.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# 一键启动本地 WebUI
# - 后端 FastAPI: 127.0.0.1:8765
# - 前端 Next.js: 127.0.0.1:3000
# - PID 和日志统一写入 .port_sessions,重复执行会清理上一次由本脚本启动的进程。
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RUN_DIR="${ROOT_DIR}/.port_sessions"
ENV_FILE="${ROOT_DIR}/.env.deploy"
if [[ -f "${ENV_FILE}" ]]; then
# shellcheck disable=SC1090
source "${ENV_FILE}"
fi
BACKEND_HOST="${CLAW_BACKEND_HOST:-0.0.0.0}"
BACKEND_PORT="${CLAW_BACKEND_PORT:-8765}"
FRONTEND_HOST="${CLAW_FRONTEND_HOST:-0.0.0.0}"
FRONTEND_PORT="${CLAW_FRONTEND_PORT:-3000}"
BACKEND_URL="http://${BACKEND_HOST}:${BACKEND_PORT}"
FRONTEND_URL="http://${FRONTEND_HOST}:${FRONTEND_PORT}"
PYTHON_BIN="${CLAW_PYTHON_BIN:-${ROOT_DIR}/.venv/bin/python}"
if [[ ! -x "${PYTHON_BIN}" ]]; then
PYTHON_BIN="python3"
fi
LOCAL_IPV4_ORIGINS="$(ifconfig 2>/dev/null | awk '/inet / && $2 !~ /^127\\./ {print $2}' | awk '!seen[$0]++' | paste -sd, -)"
DEFAULT_ALLOWED_DEV_ORIGINS="localhost,127.0.0.1,0.0.0.0"
if [[ -n "${LOCAL_IPV4_ORIGINS}" ]]; then
DEFAULT_ALLOWED_DEV_ORIGINS="${DEFAULT_ALLOWED_DEV_ORIGINS},${LOCAL_IPV4_ORIGINS}"
while IFS= read -r local_ip; do
[[ -z "${local_ip}" ]] && continue
DEFAULT_ALLOWED_DEV_ORIGINS="${DEFAULT_ALLOWED_DEV_ORIGINS},${local_ip}:${FRONTEND_PORT}"
done < <(printf "%s\n" "${LOCAL_IPV4_ORIGINS}" | tr "," "\n")
fi
mkdir -p "${RUN_DIR}"
stop_pid_file() {
local pid_file="$1"
local name="$2"
if [[ ! -f "${pid_file}" ]]; then
return
fi
local pid
pid="$(cat "${pid_file}" 2>/dev/null || true)"
if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
echo "Stopping previous ${name} (${pid})..."
kill "${pid}" 2>/dev/null || true
for _ in {1..20}; do
if ! kill -0 "${pid}" 2>/dev/null; then
break
fi
sleep 0.1
done
if kill -0 "${pid}" 2>/dev/null; then
kill -9 "${pid}" 2>/dev/null || true
fi
fi
rm -f "${pid_file}"
}
stop_port_listener() {
local port="$1"
local name="$2"
local pids
pids="$(lsof -tiTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true)"
if [[ -z "${pids}" ]]; then
return
fi
echo "Stopping ${name} listener(s) on port ${port}: ${pids//$'\n'/ }..."
while IFS= read -r pid; do
[[ -z "${pid}" ]] && continue
kill "${pid}" 2>/dev/null || true
done <<<"${pids}"
for _ in {1..20}; do
if ! lsof -tiTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1; then
return
fi
sleep 0.1
done
while IFS= read -r pid; do
[[ -z "${pid}" ]] && continue
kill -9 "${pid}" 2>/dev/null || true
done <<<"${pids}"
}
wait_for_url() {
local url="$1"
local name="$2"
local log_file="$3"
for _ in {1..80}; do
if curl -fsS "${url}" >/dev/null 2>&1; then
echo "${name} ready: ${url}"
return 0
fi
sleep 0.25
done
echo "${name} did not become ready: ${url}" >&2
echo "---- ${name} log tail ----" >&2
tail -80 "${log_file}" >&2 || true
return 1
}
shell_quote() {
printf "%q" "$1"
}
start_detached_process() {
local name="$1"
local pid_file="$2"
local log_file="$3"
local work_dir="$4"
shift 4
local command="cd $(shell_quote "${work_dir}") && echo \$\$ > $(shell_quote "${pid_file}") && exec"
local arg
for arg in "$@"; do
command+=" $(shell_quote "${arg}")"
done
command+=" >$(shell_quote "${log_file}") 2>&1"
if command -v screen >/dev/null 2>&1; then
screen -dmS "claw-${name}" bash -lc "${command}"
else
(
cd "${work_dir}"
nohup "$@" >"${log_file}" 2>&1 &
echo "$!" >"${pid_file}"
)
fi
}
stop_pid_file "${RUN_DIR}/webui-frontend.pid" "frontend"
stop_pid_file "${RUN_DIR}/webui-backend.pid" "backend"
stop_port_listener "${FRONTEND_PORT}" "frontend"
stop_port_listener "${BACKEND_PORT}" "backend"
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
echo "OPENAI_API_KEY is not configured." >&2
echo "Run scripts/deploy-ubuntu.sh once, or create ${ENV_FILE} from .env.deploy.example." >&2
fi
export OPENAI_API_KEY="${OPENAI_API_KEY:-}"
export OPENAI_BASE_URL="${OPENAI_BASE_URL:-http://model.mify.ai.srv/v1}"
export OPENAI_MODEL="${OPENAI_MODEL:-xiaomi/mimo-v2-flash}"
export OPENAI_TIMEOUT_SECONDS="${OPENAI_TIMEOUT_SECONDS:-3600}"
BACKEND_LOG="${RUN_DIR}/webui-backend.log"
FRONTEND_LOG="${RUN_DIR}/webui-frontend.log"
echo "Starting backend on ${BACKEND_URL}..."
start_detached_process "backend" "${RUN_DIR}/webui-backend.pid" "${BACKEND_LOG}" "${ROOT_DIR}" \
"${PYTHON_BIN}" -m src.gui \
--host "${BACKEND_HOST}" \
--port "${BACKEND_PORT}" \
--cwd "${ROOT_DIR}" \
--timeout-seconds "${OPENAI_TIMEOUT_SECONDS}" \
--session-dir "${RUN_DIR}/agent" \
--allow-write \
--allow-shell \
--no-browser
wait_for_url "${BACKEND_URL}/api/state" "backend" "${BACKEND_LOG}"
echo "Starting frontend on ${FRONTEND_URL}..."
export CLAW_API_URL="${BACKEND_URL}"
export CLAW_ALLOWED_DEV_ORIGINS="${CLAW_ALLOWED_DEV_ORIGINS:-${DEFAULT_ALLOWED_DEV_ORIGINS}}"
start_detached_process "frontend" "${RUN_DIR}/webui-frontend.pid" "${FRONTEND_LOG}" "${ROOT_DIR}/frontend/app" \
npm run dev -- --hostname "${FRONTEND_HOST}" --port "${FRONTEND_PORT}"
wait_for_url "${FRONTEND_URL}" "frontend" "${FRONTEND_LOG}"
cat <<EOF
WebUI started.
Frontend: ${FRONTEND_URL}
Backend : ${BACKEND_URL}
Logs : ${RUN_DIR}/webui-frontend.log
${RUN_DIR}/webui-backend.log
Stop:
kill \$(cat "${RUN_DIR}/webui-frontend.pid") \$(cat "${RUN_DIR}/webui-backend.pid")
EOF