feat(web): v1 图标头像迁移 + 仅 chat 按 <p> 拆气泡 + 一键控制脚本 + <p> 分段约定
【v1 图标/头像迁移】 - 复制 v1 前端资源到 v2:Cyrene_Avatar(2ndForm 192x192 + 3rdForm 3F-Q-Happy-1/2)、User_Avatar。 - favicon/apple-touch-icon → Cyrene-2F-N-Happy-1-192x192.png;TopBar 品牌图标 + 聊天助手头像 → Cyrene-3F-Q-Happy-1.png(圆形头像)。icon.svg 保留作 MD3 组件引用不变。 【仅 chat 按 <p> 拆气泡(主人约束:仅 chat 生效,其他 mode 不解析)】 - cyrene.yaml 聊天节奏:想分多条用 <p>第一条</p><p>第二条</p>(每个=<p> 一条独立消息), \n 是句内换行;每段尽量短。 - chat.tsx:SplitChatBubble 仅 mode===chat 时拆 <p>;非 chat → MessageBubble 原样(不解析)。 <p> 是拆气泡信号(先拆段),段内 renderContent 把 <action>/<narration> 变灰斜体,两层分层。 首段完整(头像/思维链/工具卡)、后续段精简(仅正文+灰斜体);user 同理;流式未闭合<p>净化不闪标签。 - ChatShell.tsx:MessageBubble → SplitChatBubble。 【scripts/ctrl.sh 一键控制】 - 无参=start;支持 start/stop/restart/status;setsid 脱离防误清、PID 文件+端口探测、health 确认。 go build 全绿(后端未动);前端 tsc/vite build 75模块绿。
This commit is contained in:
1 parent
33eeadfc38
commit
950bea9758
10 files changed
+277
-5
No files matched your search
+135
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cyrene v2 本地一键控制:启动 / 重启 / 终止 后台 cyrene 进程。
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/ctrl.sh # 启动(默认)
|
||||
# bash scripts/ctrl.sh start # 启动
|
||||
# bash scripts/ctrl.sh restart # 重启
|
||||
# bash scripts/ctrl.sh stop # 终止
|
||||
# bash scripts/ctrl.sh status # 查看状态
|
||||
#
|
||||
# 说明:
|
||||
# - 管理单个 cyrene 后端进程(单二进制)。llama-server / PG 若需一并管理,
|
||||
# 可另用 docker / scripts/dev-up.sh。
|
||||
# - cyrene 在仓库根下运行(读 .env 的 PORT / DATA_DIR 等),日志写 ~/cy.log。
|
||||
# - 用 setsid + nohup 脱离进程组,避免终端/后台 shell 超时误杀。
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
APP_BIN="$HOME/apps/cyrene/cyrene"
|
||||
PID_FILE="$HOME/.cyrene.pid"
|
||||
LOG_FILE="$HOME/cy.log"
|
||||
|
||||
# 端口优先读 .env 的 PORT(用户可改),找不到才回退 10080
|
||||
GW_PORT="$(grep -E '^PORT=' "$REPO/.env" 2>/dev/null | head -1 | cut -d= -f2)"
|
||||
GW_PORT="${GW_PORT:-10080}"
|
||||
HEALTH_URL="http://127.0.0.1:$GW_PORT/api/v1/health"
|
||||
|
||||
# 读取进程 PID:先看 PID 文件,没有则从监听端口探测(能识别外部已起的 cyrene)
|
||||
read_pid() {
|
||||
# 1) PID 文件优先
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
local pid
|
||||
pid="$(cat "$PID_FILE" 2>/dev/null)"
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
echo "$pid"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# 2) 端口探测兜底(ss 找监听 GW_PORT 的 cyrene 进程)
|
||||
local p
|
||||
p="$(ss -tlnp 2>/dev/null | grep ":$GW_PORT" | grep -oE 'pid=[0-9]+' | head -1 | sed -E 's/[^0-9]//g')"
|
||||
if [ -n "$p" ] && kill -0 "$p" 2>/dev/null; then
|
||||
echo "$p"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
is_running() {
|
||||
read_pid >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# 健康检查:进程在 + HTTP 200
|
||||
healthy() {
|
||||
read_pid >/dev/null 2>&1 && curl -s --max-time 2 "$HEALTH_URL" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "cyrene 已在运行 (PID $(read_pid)),跳过。用 restart 重启。"
|
||||
return 0
|
||||
fi
|
||||
[ -x "$APP_BIN" ] || { echo "未找到二进制 $APP_BIN,请先 go build"; return 1; }
|
||||
echo "启动 cyrene → $APP_BIN (端口 $GW_PORT)"
|
||||
( cd "$REPO" && setsid nohup "$APP_BIN" > "$LOG_FILE" 2>&1 < /dev/null & )
|
||||
# 等待健康
|
||||
local i
|
||||
for i in $(seq 1 20); do
|
||||
sleep 0.5
|
||||
if healthy; then
|
||||
echo "已启动: http://localhost:$GW_PORT (日志 $LOG_FILE)"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "启动后 ${GW_PORT} 未就绪,请查看日志 $LOG_FILE"
|
||||
return 1
|
||||
}
|
||||
|
||||
stop() {
|
||||
local pid
|
||||
if read_pid >/dev/null 2>&1; then
|
||||
pid="$(read_pid)"
|
||||
kill "$pid" 2>/dev/null || true
|
||||
echo "已发送终止信号 → PID $pid"
|
||||
local i
|
||||
for i in $(seq 1 20); do
|
||||
sleep 0.4
|
||||
kill -0 "$pid" 2>/dev/null || { echo "已退出"; rm -f "$PID_FILE"; return 0; }
|
||||
done
|
||||
echo "进程未在 8s 内退出,强制 kill -9"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
rm -f "$PID_FILE"
|
||||
else
|
||||
echo "cyrene 未在运行(无 PID 文件或进程已退出)"
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
restart() {
|
||||
echo "重启 cyrene…"
|
||||
stop
|
||||
sleep 1
|
||||
start
|
||||
}
|
||||
|
||||
status() {
|
||||
if healthy; then
|
||||
echo "cyrene: 运行中 (PID $(read_pid)) · 端口 $GW_PORT · health ok"
|
||||
elif is_running; then
|
||||
echo "cyrene: 进程在 (PID $(read_pid)) 但 health 未就绪 ($HEALTH_URL)"
|
||||
else
|
||||
echo "cyrene: 未运行"
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "用法: bash scripts/ctrl.sh [start|stop|restart|status] (无参 = start)"
|
||||
}
|
||||
|
||||
# start 成功后把探测到的 PID 写入文件,供后续 stop 精确管理
|
||||
record_pid() {
|
||||
if healthy; then
|
||||
local p
|
||||
p="$(ss -tlnp 2>/dev/null | grep ":$GW_PORT" | grep -oE 'pid=[0-9]+' | head -1 | sed -E 's/[^0-9]//g')"
|
||||
[ -n "$p" ] && echo "$p" > "$PID_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start) start; record_pid ;;
|
||||
restart) restart; record_pid ;;
|
||||
stop) stop ;;
|
||||
status) status ;;
|
||||
*) usage; exit 1 ;;
|
||||
esac
|
||||
Reference in new issue
Block a user