fix: 修改一下生图的 bug

This commit is contained in:
2026-08-31 21:56:48 +08:00
parent 2a667b4f3b
commit 8b54f7ed1e
14 changed files with 636 additions and 47 deletions
+107 -27
View File
@@ -1,11 +1,112 @@
#!/bin/zsh
# 双击本文件,或在终端执行:./start.command
cd "$(dirname "$0")"
cd "$(dirname "$0")" || exit 1
PROJECT_DIR="$PWD"
BACKEND_HOST="127.0.0.1"
BACKEND_PORT="8800"
BACKEND_URL="http://${BACKEND_HOST}:${BACKEND_PORT}"
STUDIO_URL="http://localhost:8900"
RUNTIME_DIR="$PROJECT_DIR/data/runtime"
INSTANCE_PID_FILE="$RUNTIME_DIR/start-command.pid"
# 由当前 start.command 管理的子进程。它们都在清理函数里按 PID 树结束,
# 不扫描或误杀其他项目的 Python / Node 进程。
BACKEND_PID=""
STUDIO_PID=""
WORKER_SUPERVISOR_PID=""
COLLECTOR_WATCH_PID=""
COLLECTOR_STAMP=""
CLEANUP_DONE=false
stop_process_tree() {
local pid="$1"
local child
[[ "$pid" == <-> ]] || return 0
kill -0 "$pid" 2>/dev/null || return 0
# 先停止子进程,避免 Worker 守护循环在父进程退出前重新拉起 Worker。
for child in $(pgrep -P "$pid" 2>/dev/null); do
stop_process_tree "$child"
done
kill -TERM "$pid" 2>/dev/null || true
}
cleanup() {
[[ "$CLEANUP_DONE" == true ]] && return
CLEANUP_DONE=true
stop_process_tree "$BACKEND_PID"
stop_process_tree "$STUDIO_PID"
stop_process_tree "$WORKER_SUPERVISOR_PID"
stop_process_tree "$COLLECTOR_WATCH_PID"
[[ -n "$COLLECTOR_STAMP" ]] && rm -f "$COLLECTOR_STAMP" 2>/dev/null
if [[ -f "$INSTANCE_PID_FILE" ]] && [[ "$(<"$INSTANCE_PID_FILE")" == "$$" ]]; then
rm -f "$INSTANCE_PID_FILE"
fi
}
on_signal() {
exit 0
}
stop_previous_instance() {
[[ -f "$INSTANCE_PID_FILE" ]] || return 0
local previous_pid command cwd
previous_pid="$(<"$INSTANCE_PID_FILE")"
if [[ "$previous_pid" != <-> ]] || ! kill -0 "$previous_pid" 2>/dev/null; then
rm -f "$INSTANCE_PID_FILE"
return 0
fi
command="$(ps -p "$previous_pid" -o command= 2>/dev/null)"
cwd="$(lsof -a -p "$previous_pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n 1)"
if [[ "$command" != *"start.command"* || "$cwd" != "$PROJECT_DIR" ]]; then
echo "⚠️ 发现过期的启动记录,未匹配到本项目进程,已忽略。"
rm -f "$INSTANCE_PID_FILE"
return 0
fi
echo "检测到已有工具台实例(PID: $previous_pid),正在停止后重新启动…"
stop_process_tree "$previous_pid"
for _ in {1..50}; do
kill -0 "$previous_pid" 2>/dev/null || break
sleep 0.1
done
if kill -0 "$previous_pid" 2>/dev/null; then
echo "⚠️ 旧实例未能停止;为避免启动重复 Worker,本次不启动新实例。"
exit 1
fi
rm -f "$INSTANCE_PID_FILE"
}
stop_unmanaged_project_workers() {
# PID 文件只能管理此前由 start.command 拉起的实例;这里额外收敛曾被手动
# 启动、但 cwd 明确属于当前项目的 Worker。绝不按 Python 名称做全局清理。
local pid parent_pid command cwd parent_command parent_cwd
for pid in $(pgrep -f 'server/worker\.py' 2>/dev/null); do
command="$(ps -p "$pid" -o command= 2>/dev/null)"
cwd="$(lsof -a -p "$pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n 1)"
[[ "$command" == *"server/worker.py"* && "$cwd" == "$PROJECT_DIR" ]] || continue
parent_pid="$(ps -p "$pid" -o ppid= 2>/dev/null | tr -d ' ')"
parent_command="$(ps -p "$parent_pid" -o command= 2>/dev/null)"
parent_cwd="$(lsof -a -p "$parent_pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n 1)"
echo "检测到遗留 Worker(PID: $pid),正在停止…"
if [[ "$parent_command" == *"start.command"* && "$parent_cwd" == "$PROJECT_DIR" ]]; then
# 这是脱离 PID 文件的旧守护循环;连同父进程停掉,避免它 2 秒后重拉 Worker。
stop_process_tree "$parent_pid"
else
# 手动启动的 Worker 只停止自身,不触碰其可能属于其他工具的父终端。
kill -TERM "$pid" 2>/dev/null || true
fi
done
}
mkdir -p "$RUNTIME_DIR" || exit 1
stop_previous_instance
stop_unmanaged_project_workers
print -r -- "$$" > "$INSTANCE_PID_FILE"
trap cleanup EXIT
trap on_signal INT TERM HUP
if [[ ! -d .venv ]]; then
echo "未找到 .venv,正在创建…"
@@ -32,7 +133,6 @@ fi
# ---- 采集插件(extensions/collector):启动时构建一次,之后监听代码改动自动重建 ----
# 产物 .output/chrome-mv3 由 Chrome 以解压方式加载;重建后仍需去 chrome://extensions 点刷新
COLLECTOR_WATCH_PID=""
COLLECTOR_STAMP="$(mktemp -u /tmp/osk-collector-stamp.XXXXXX)"
watch_collector() {
touch "$COLLECTOR_STAMP"
@@ -68,29 +168,6 @@ else
fi
echo
# ---- 同步并启动前端(studio:Vite 开发服务器,/api 代理到后端 8800)----
STUDIO_PID=""
WORKER_SUPERVISOR_PID=""
cleanup() {
if [[ -n "$WORKER_SUPERVISOR_PID" ]] && kill -0 "$WORKER_SUPERVISOR_PID" 2>/dev/null; then
# 先终止 Worker 子进程,再结束守护循环;避免退出 start.command 后残留处理器。
pkill -P "$WORKER_SUPERVISOR_PID" 2>/dev/null
kill "$WORKER_SUPERVISOR_PID" 2>/dev/null
fi
if [[ -n "$STUDIO_PID" ]] && kill -0 "$STUDIO_PID" 2>/dev/null; then
kill "$STUDIO_PID" 2>/dev/null
sleep 0.2
# 兜底:确保 vite 及其子进程退出
lsof -ti :8900 | xargs kill 2>/dev/null
fi
if [[ -n "$COLLECTOR_WATCH_PID" ]] && kill -0 "$COLLECTOR_WATCH_PID" 2>/dev/null; then
pkill -P "$COLLECTOR_WATCH_PID" 2>/dev/null # 可能正在跑的 build 子进程
kill "$COLLECTOR_WATCH_PID" 2>/dev/null
fi
rm -f "$COLLECTOR_STAMP" 2>/dev/null
}
trap cleanup EXIT
# ---- 持久化后台任务 Worker(与 uvicorn 独立,服务重启不会中断正在轮询的模型任务)----
# Worker 因配置/网络等未捕获错误退出时,2 秒后自动拉起;否则任务会长期停在「排队中」。
supervise_worker() {
@@ -124,8 +201,11 @@ echo
echo "按 Ctrl+C 可停止全部服务"
echo
# --app-dir server 让 uvicorn 在 server/ 下找 main:app,工作目录仍是仓库根(.env 在此)
uvicorn main:app --app-dir server --reload --reload-include '*.yaml' --host "$BACKEND_HOST" --port "$BACKEND_PORT"
# --app-dir server 让 uvicorn 在 server/ 下找 main:app,工作目录仍是仓库根(.env 在此)。
# 后端改为后台子进程 + wait:start.command 本身始终存活,PID 文件和 EXIT 清理才能可靠生效。
uvicorn main:app --app-dir server --reload --reload-include '*.yaml' --host "$BACKEND_HOST" --port "$BACKEND_PORT" &
BACKEND_PID=$!
wait "$BACKEND_PID"
echo
echo "服务已停止。按回车关闭窗口…"