首页
/ LobeHub 本地开发服务器(Local Dev Server)完整操作指南:Agent 验收测试后端的启动、重启与排障

LobeHub 本地开发服务器(Local Dev Server)完整操作指南:Agent 验收测试后端的启动、重启与排障

2026-09-07 22:49:09作者:廉皓灿Ida

本指南以 LobeHub 仓库中的 dev-server.md 为核心,系统梳理了 agent 验收(acceptance)测试体系下本地开发服务器的启动、重启、端口解析与故障排查全流程。这套服务器是 CLI、Electron、Web 三类测试表面(test surface)共同访问的后端唯一事实源(single source of truth),掌握它你就能在 LobeHub 仓库中稳定拉起"与生产异步执行方式一致"的本地测试环境,并快速定位 ECONNREFUSED、端口占用、队列模式失效等典型问题。

它在验收测试流程中的位置:先理解为何需要这份文档

LobeHub 仓库的 acceptance 体系由三层文档共同定义契约、流程与命令:

  • PROCESS.md 拥有"流程"——一次验证运行如何被计划、批准、执行、发布与拆除,其 Step 2(Environment and auth)要求在任何测试之前先解析环境并启动本地服务;
  • PROJECT.md 拥有"命令"——端口、服务、表面(surface)与探针的 LobeHub 专属入口;
  • dev-server.md 则被定位为"开始/重启后端的单一事实源",所有测试表面(CLI、Electron、Web)最终访问的都是这套服务器。

因此文档开篇就强调两条铁律:

  1. 先解析端口再启动:在启动或探测任何本地测试表面之前,先按 PROCESS.md 的 Step 2 运行 test-env.sh 解析环境;
  2. 不要硬编码端口表:端口从环境解析器读取,而不是从任何静态表格猜。

从源码看,.agents/acceptance/scripts/test-env.sh 正是这套"环境解析器":它刻意设计为只读,按 .env → .env.$NODE_ENV → .env.local → .env.$NODE_ENV.local 的顺序加载文件,再叠加 shell 环境变量作为最高优先级,最终打印 APP_URLPORTSERVER_URLAUTH_TRUSTED_ORIGINSSPA_PORTMOBILE_SPA_PORTDESKTOP_PORT 七个键的值及其来源(文件/shell/自动分配/推断)。

三种运行模式与各自端口来源

命令 运行内容 端口来源
pnpm run dev:next Next.js 后端(API + auth) PORT
bun run dev 全栈(Next.js + Vite SPA,经由 devStartupSequence PORT + SPA_PORT
bun run dev:spa 仅 Vite SPA,API 代理到 PORT SPA_PORT

三种模式对应三种测试诉求:

  • pnpm run dev:next:只跑 Next.js 后端(next dev -p 3010,见 package.json 中的 dev:next 脚本)。适合 CLI 表面与后端接口验证,但不会提供 SPA 页面;
  • bun run dev:全栈模式,是 Web smoke 测试的硬性前提。它执行根脚本 devtsx scripts/devStartupSequence.mts),由编排器同时拉起 Next 与 Vite,且 Next 会把 SPA HTML 从 Vite 代理过来。从 scripts/devStartupSequence.mts 源码可见其端口决策逻辑:Next 端口按 -p CLI 参数 > PORT 环境变量 > 从 3010 起探测第一个空闲端口 的优先级解析;Vite 端口在非移动端取 SPA_PORT(无显式值时从 9876 起探测空闲端口),并把结果同时写入 SPA_PORTVITE_DEV_PORT 两个环境变量——后者是 Next 侧读取 Vite 服务位置的唯一契约,不依赖任何端口文件;
  • bun run dev:spa:仅 Vite SPA,将 API 请求代理到 PORT。PROJECT.md 还提醒:需要 Next 代理 SPA HTML 时不能只用 dev-next,而 dev:spa 在"本地前端 + 生产后端"场景下会打印一个 _dangerous_local_dev_proxy URL,只用于验证前端对生产数据的行为,不可用于测试后端分支改动。

cloud 仓库工作树到默认端口的映射

文档特别说明:当本仓库作为 lobehub/ 子模块存在于 cloud 仓库时,只要 .env 与 shell 环境都未提供取值,工作树名称才会映射到下面的兜底默认值:

工作区目录 默认 SERVER_URL
lobehub http://localhost:3010
lobehub-cloud http://localhost:3020
lobehub-cloud-1 http://localhost:3021
lobehub-cloud-N http://localhost:$((3020 + N))

init-dev-env.shsetup-auth.sh 的源码可以印证这一映射的实现:两者在解析工作区根目录时,若当前目录名为 lobehub 且其父目录名匹配 lobehub-cloud*,就会把 WORKSPACE_ROOT 上提为 cloud 父目录,并据此计算偏移量(default_port 函数把 3020 作为 cloud 的基端口,普通 lobehub 目录回落 3010)。

test-env.shsetup-auth.sh 都会优先使用已解析的环境变量,上述工作树默认值只是最后兜底。因此文档给出了测试时的关键纪律:当使用非标准端口时,以 dev 服务器的终端输出为最终事实源,并把解析结果导出给每一条 agent-testing 命令:

export SERVER_URL=http://localhost:<port-from-dev-output>

端口与环境的解析:两种启动路径的分水岭

dev-server.md 把启动路径按"是否存在仓库根 .env"一分为二,这与 PROCESS.md Step 2PROJECT.md §2 保持一致:

  • 存在根 .env:一律使用现有本地配置,直接 pnpm run dev:next / bun run dev不要调用任何 init-dev-env.sh 子命令;
  • 不存在 .env:使用自包含的 agent-testing 环境,由 .agents/acceptance/scripts/init-dev-env.sh 完成整套引导。

init-dev-env.sh 是理解后文所有命令的关键:它自带守卫——一旦仓库根目录存在 .env,除帮助外的所有子命令都会立即退出,从机制上保证"绝不覆盖用户已有配置"。它的完整子命令一览(对应源码注释区与 case 分发):

子命令 作用
env 打印可 source 的 shell 导出
write [file] 写出可 source 的 env 文件(默认 .records/env/agent-testing-dev.env
setup-db 启动本地 Postgres(paradedb 容器 lobehub-agent-testing-postgres)与 Redis(lobehub-agent-testing-redis)并跑迁移
migrate 对已配置数据库执行 bun run db:migrate
seed-user 植入基线测试用户 + CLI API key,并写出 .records/env/agent-testing-cli.env
qstash 在独立终端运行本地 Upstash QStash dev 服务(默认 127.0.0.1:8080
s3 运行本地 s3rver 对象存储(默认 29000 端口)
preflight 检查 agent-runtime 前置条件(QStash + S3 + Redis + dev server)
dev-next 以该环境 exec pnpm exec next dev -p $SERVER_PORT
dev 以该环境 exec bun run dev
stop-dev/clean 停止由本脚本启动的 dev 进程树(保留 DB/Redis/S3 数据)
clean-db/clean-s3 移除受管容器 / 本地 S3 测试数据

在无 .env 模式下,脚本会通过 _load_or_alloc_ports 自动分配不与已有实例冲突的 SERVER_PORT/SPA_PORT(从 20000+ 随机探测、首次分配后持久化到 .records/env/agent-testing-ports.env),并把 AGENT_RUNTIME_MODE 默认设为 queue。注意其 dev-next 实现(源码 cmd_dev_next)有一个易被忽略的细节:直接 pnpm exec next dev -p "$SERVER_PORT" 而非走 dev:next 脚本,是因为子模块的 dev:next 硬编码了 -p 3010,当端口被自动分配为非 3010 时会绑定错误端口。

健康检查:探测前先确认服务器在线

无论哪种模式,探测(probe)前的标准健康检查都是一条 curl:

curl -s -o /dev/null -w '%{http_code}' "$SERVER_URL/"

只要端口上有 HTTP 监听者即视为就绪——脚本源码中 _http_reachable 的判定正是"任何非 000 的 HTTP 状态码即算可达"。这套判定在 setup-auth.shcheck_server 里也被复用(接受 2xx/3xx)。

启动与重启:标准操作清单

文档给出的启动/重启命令矩阵如下,可直接复制执行:

# 只启动后端。
# 有根 .env:使用现有本地配置。
# Agent 运行时队列模式是必需的,用于镜像生产环境的异步执行。
AGENT_RUNTIME_MODE=queue pnpm run dev:next

# 无根 .env:使用自包含的 agent-testing 环境。
.agents/acceptance/scripts/init-dev-env.sh dev-next

# 全栈 SPA + 后端。Web smoke 测试必需。
# 有根 .env:
AGENT_RUNTIME_MODE=queue bun run dev

# 无根 .env:
.agents/acceptance/scripts/init-dev-env.sh dev

# 本地 QStash。仅在测试 workflow 路径时于独立终端运行。
.agents/acceptance/scripts/init-dev-env.sh qstash

# 重启——拉取服务器端代码改动所必需。
# 对由 init-dev-env.sh 启动的无 .env 服务器,只停它拥有的进程树:
.agents/acceptance/scripts/init-dev-env.sh stop-dev
.agents/acceptance/scripts/init-dev-env.sh dev-next

为什么 AGENT_RUNTIME_MODE=queue 如此重要

文档要求队列模式"以镜像生产异步执行",这背后有明确的工程原因(在 PROJECT.md §6 中被标注为硬性约束):queue 模式(本仓库与生产环境的默认值)下,创建 agent 操作会先向本地 QStash(127.0.0.1:8080)POST 投递;一旦 QStash 未运行,agent 运行会在任何 LLM 调用之前死于 ECONNREFUSED 127.0.0.1:8080 / fetch failed——没有 trace 被记录,且错误读起来与测试环境无关。init-dev-env.sh 默认就设置 AGENT_RUNTIME_MODE=queue(见 apply_env),并用 _qstash_reachable 这类"以 QStash REST 契约为准"的探针(/v2/schedules 无 token 应 401、带配置 token 应 200)来识别真正的 QStash 监听者,避免 8080 端口被其他服务占位时误判。

因此官方推荐的完整无 .env 引导序列是(见 PROJECT.md §2):

.agents/acceptance/scripts/init-dev-env.sh setup-db
.agents/acceptance/scripts/init-dev-env.sh s3       # 终端 B,保持运行
.agents/acceptance/scripts/init-dev-env.sh seed-user
.agents/acceptance/scripts/init-dev-env.sh qstash   # 终端 B,保持运行
.agents/acceptance/scripts/init-dev-env.sh dev

停止时的进程所有权纪律

stop-dev/clean 不是粗暴的进程名匹配。从源码 cmd_stop_dev 看,它读取 write_dev_state 持久化的状态文件(.records/runtime/agent-testing-dev.state,记录 PID、进程启动时间、cwd 与模式),经 state_owns_process 三项核验(进程存在、启动时间一致、cwd 属于仓库根)后,再对整棵后代进程树发 SIGTERM,超时才 SIGKILL。这正是 PROCESS.md Step 6 所要求的"只停本轮启动的东西"在脚本层的落地。配套测试 init-dev-env.test.sh 验证了这些行为:重复启动不会替换活跃的所有权记录、clean A 不会误杀同级 B、篡改过 PROCESS_START 的陈旧状态会被拒绝清理。

什么时候必须重启服务器

Next.js 热更新可能无法捕获 workspace 包内的改动——文档的判定原则是"存疑即重启"。以下是官方的重启矩阵:

改动位置 需要重启?
apps/server/src/(routers、services、modules)
apps/server/src/router-hono/
packages/database/(模型)
packages/types/
packages/prompts/
apps/cli/(CLI 从源码直接运行)

其中 apps/server 位于根 pnpm workspace 之内(pnpm-workspace.yaml 声明的 workspace 列表),而 apps/cli 需要独立安装且直接以 bun src/index.ts 从源码运行(无构建步骤),所以 CLI 侧代码改动即时生效、无需重启。仓库根 package.jsondev:next 等脚本与 PROJECT.md §1 中"根 workspace 不覆盖 apps/desktopapps/cli"的说明可互为印证——测试前必须在涉及的独立 app 内单独 pnpm install,否则会出现 workspace 包解析失败。

故障排查速查表

问题 解决方案
ECONNREFUSED 服务器未运行——启动它
端口上的 EADDRINUSE 检查监听者;仅当该进程是本轮自己启动的才停掉它。绝不只凭端口杀掉未知 PID
数据陈旧 / 行为仍是旧的 服务器需要重启以拾取代码改动(对照上文重启矩阵)
Agent 调用以内联方式执行 设置 AGENT_RUNTIME_MODE=queue,确认 REDIS_URL 已配置,然后重启服务器
队列模式需要 Redis 运行 init-dev-env.sh setup-db,或为已有 Redis 提供 REDIS_URL=redis://...
QStash workflow 失败 启动 init-dev-env.sh qstash,并确保 dev 服务器继承了脚本的 QSTASH_* 环境变量

这些条目同样能在脚本源码中找到依据:

  • .env 模式下,DATABASE_URL 默认指向本地 5433 端口 Postgres、REDIS_URL 指向 6380 端口 Redis(容器 lobehub-agent-testing-redis),因此队列模式的运行时状态存储天然需要 setup-db 提供的 Redis;
  • cmd_qstash 通过 pnpm run qstash(即 pnpx @upstash/qstash-cli@latest dev)拉起本地 QStash,并在 apply_env 中统一设置 QSTASH_URLQSTASH_TOKENQSTASH_CURRENT_SIGNING_KEYQSTASH_NEXT_SIGNING_KEY——只要服务器由 dev/dev-next 子命令以 exec 继承该环境启动,它就能拿到这些 QSTASH_* 变量;
  • EADDRINUSE 与所有权纪律在 _pick_free_port(首次分配避开占用端口)与 stop_owned_process_tree(只杀自有进程树)中均有体现。

边界:Marketplace 端点不在本地认证闸门内

文档在末尾特意划出一条范围边界:marketplace/community 端点不属于本地 agent-testing 认证闸门的一部分。除非改动明确针对 marketplace 行为,否则不要把本地产品链路验证阻塞在 marketplace API 认证上。这条边界在 PROCESS.mdPROJECT.md §6 中也被重复强调("Marketplace/community endpoints are not part of the local auth gate"),说明它是一条贯穿验收流程、需要测试 Agent 始终遵守的环境约束。

小结

LobeHub 的本地 dev server 是整套 agent 验收体系的地基:以 test-env.sh 解析端口、以"有无根 .env"划分两条启动路径、以 AGENT_RUNTIME_MODE=queue 对齐生产异步语义、以进程所有权状态文件保证可安全停启,并以重启矩阵与故障表兜底日常排障。建议将本文与 PROJECT.mdPROCESS.md 配合阅读:前者给你命令,后者给你流程,而 dev-server.md 在它们之间充当启动/重启后端的"单一事实源"。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388