Files
Genarrative/.codex/skills/genarrative-dev-stack-port-routing/SKILL.md
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

11 KiB
Raw Blame History

name, description, license, metadata
name description license metadata
genarrative-dev-stack-port-routing 在 Genarrative 中修改 npm run dev / dev:spacetime / dev:api-server / dev:bgfilter-worker / dev:web / dev:admin-web 的本地启动端口、端口可用性探测、端口漂移、SpacetimeDB publish server、Rust 进程环境变量、Vite 代理目标和后台 admin-web 启动串联时使用。 MIT
codex
tags related_skills
Genarrative
dev-stack
端口探测
Vite
api-server
SpacetimeDB
npm-run-dev
genarrative-admin-backoffice

Genarrative 本地 dev 启动端口与代理目标串联流程

用于维护 Genarrative 本地开发栈启动脚本,重点覆盖 npm run dev 与五个 dev:* 单模块命令的端口检查、端口漂移和后续流程目标传递。

适用场景

  • 修改 scripts/dev.mjsscripts/dev-utils.mjsscripts/dev-stack-port-utils.mjs 或 AI 游戏创作客户端 dev 启动器。
  • 处理 3000310131028082 等端口被占用导致本地开发栈启动失败。
  • 排查 Vite 代理仍指向旧 api-server 端口、前端打开了旧 dev server、后台代理错配。
  • 调整 SpacetimeDB standalone、publish、Rust api-server、主站 Vite、后台 Vite 的启动顺序。
  • 修改本地联调文档或 docs/project-memory/shared-memory/pitfalls.md 中的 dev 启动口径。

当前端口职责

默认优先端口:

  1. 主站 Vite3000,对浏览器通常展示为 http://127.0.0.1:<web-port>/
  2. Rust api-server8082,健康检查为 http://127.0.0.1:<api-port>/healthz
  3. SpacetimeDB standalone3101,健康检查为 http://127.0.0.1:<spacetime-port>/v1/ping
  4. 后台 Vite3102,后台地址为 http://127.0.0.1:<admin-web-port>/admin/
  5. 独立 BgFilter worker8083,就绪检查为 http://127.0.0.1:<bgfilter-worker-port>/readyz
  6. AI 游戏创作 Vite:非 Linux 兼容首选 3080Linux 使用当前用户端口段的 start + 5

端口不可用时,脚本会从优先端口开始向后寻找可用端口。后续流程必须以解析后的实际端口为准,不能继续使用默认端口。

Linux 多用户并发开发时,GENARRATIVE_DEV_PORT_RANGE--port-range 会先向系统级注册表 /var/tmp/genarrative-dev-port-ranges/registry.json 申请一个端口段,再把该段映射为 web = startapi = start + 1spacetime = start + 2adminWeb = start + 3bgfilterWorker = start + 4agcVite = start + 5。注册表锁文件是 /var/tmp/genarrative-dev-port-ranges/registry.lock,可通过 GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR 覆盖目录。自动分配从 10000-10099 起,每次占用 100 个端口块,后续块按 10100-1019910200-10299 递增;当前口径是“一个用户固定占用一个段,后续启动继续复用这段并在段内漂移”;该注册表只在 Linux 上生效;Windows 继续沿用原有统一端口探测和漂移逻辑,不读系统级注册表。

实现入口

  • package.json
    • dev:执行 node scripts/dev.mjs,启动完整五服务。
    • dev:spacetime / dev:api-server / dev:bgfilter-worker / dev:web / dev:admin-web:执行 node scripts/dev.mjs <module>dev:api-server 会安全带起其依赖的 BgFilter worker。
  • scripts/dev-stack-port-utils.mjs
    • isPortAvailable(...):探测端口是否可监听。
    • findAvailablePort(...):从优先端口向后寻找可用端口,0 表示申请临时端口。
    • resolveDevStackPorts(...):一次性解析 SpacetimeDB、api-server、主站 Vite、后台 Vite、BgFilter worker 端口,并避免本次解析结果互相冲突。
    • Linux 注册表分配:reserveLinuxDevPortRange(...) / releaseLinuxDevPortRange(...),仅在 Linux 上启用系统级端口段登记与用户段复用,自动分配从 10000-10099 起。
    • CLI 模式:node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:3101 api:127.0.0.1:8082 web:0.0.0.0:3000 adminWeb:127.0.0.1:3102 bgfilterWorker:127.0.0.1:8083
  • scripts/dev.mjs
    • 解析 CLI 参数后统一计算 client host、端口、SPACETIME_SERVERRUST_SERVER_TARGET
    • 完整栈按 SpacetimeDB、publish、BgFilter worker readiness、api-server readiness、主站 Vite、后台 Vite 顺序启动。
    • Linux 下会先申请系统级端口段并映射成六个预留槽位;主 dev 栈使用前五个,AGC Vite 使用 start + 5,自动分配从 10000-10099 起。Windows 的主 dev 栈和 AGC 则各自沿用统一端口探测与漂移逻辑。
    • 完整栈和 dev:api-server 把两个 Rust 进程作为同一重启单元,先全部停止,再先启动 BgFilter worker、后启动 api-server;不要为同一份 Rust 源码创建两个并发 cargo watcher。
    • 单模块命令复用同一套参数和 env 解析。
  • apps/ai-game-creator-shell/scripts/dev-port.mjs
    • 复用系统级用户端口段,解析 AGC Vite 的 start + 5 首选槽位。
    • 把最终端口通过 GENARRATIVE_AGC_VITE_PORT 同步给 beforeDevCommand 和配套后端端口解析器,通过 Tauri CLI --config 同步 build.devUrl,并通过 Vite CLI --port 同步 strictPort 监听。

必须保持的传递链路

npm run dev 和五个 dev:* 单模块命令中端口解析后,必须同步到以下位置:

  1. SpacetimeDB 启动:spacetime start --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"
  2. SpacetimeDB 发布:spacetime publish ... --server "${SPACETIME_SERVER}"
  3. Rust api-serverGENARRATIVE_API_HOSTGENARRATIVE_API_PORTGENARRATIVE_SPACETIME_SERVER_URLGENARRATIVE_SPACETIME_DATABASE
  4. api-server 健康检查:wait_for_api_server "${RUST_SERVER_TARGET}/healthz" ...
  5. BgFilter workerGENARRATIVE_PROCESS_ROLE=bgfilter-worker、解析后的 HOST / PORT、与父 API 相同的 GENARRATIVE_BGFILTER_WORKER_BASE_URL / GENARRATIVE_BGFILTER_INTERNAL_TOKEN,以及显式有效的 N / Q
  6. BgFilter worker readiness:父 API 启动前检查解析后地址的 /readyz
  7. 主站 ViteRUST_SERVER_TARGETGENARRATIVE_RUNTIME_SERVER_TARGETADMIN_WEB_TARGETADMIN_WEB_PORT--port=${WEB_PORT}--host=${WEB_HOST}
  8. 后台 ViteADMIN_API_TARGETGENARRATIVE_API_TARGETGENARRATIVE_API_PORT--port=${ADMIN_WEB_PORT}
  9. 控制台日志:[dev:ports][dev] web/admin web/api-server/bgfilter-worker/spacetime 必须显示最终实际地址。
  10. Linux 端口段注册:[dev] port-range:[dev] port-range-registry: 只在 Linux 输出,Windows 不应依赖系统级注册表。
  11. AI 游戏创作客户端:外层启动器解析最终 AGC Vite 端口后,通过 Tauri CLI --config 覆盖 build.devUrl,把同一 GENARRATIVE_AGC_VITE_PORT 传给 beforeDevCommand 与配套后端端口解析器,并用 Vite CLI --port 启动严格监听;后端端口漂移必须跳过该预留端口。

如果只改了其中一段,通常会出现:浏览器打开的前端可用,但 /api/* 代理到旧端口;后台页面可用但后台 API 失败;SpacetimeDB 启动在新端口但 publish 仍发往旧端口。

修改流程

  1. 先读当前脚本和文档:
    • scripts/dev-stack-port-utils.mjs
    • scripts/dev.mjs
    • scripts/dev-utils.mjs
    • docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
    • docs/project-memory/shared-memory/pitfalls.md
  2. 优先改公共端口工具,不要把端口探测逻辑复制到多个脚本。
  3. 修改 scripts/dev.mjs 时确认变量顺序:先解析参数和端口,再构造 SPACETIME_SERVER / RUST_SERVER_TARGET,最后启动对应 service。
  4. 修改 watch 时保持模块边界:SpacetimeDB 只监听 spacetime-module 且改动后重新 publish,不重启 standalone 宿主;api-server 排除 spacetime-moduleweb/admin-web 源码变化交给 Vite 自身 HMR,外层调度器不要再监听前端目录重启 Vite。
  5. 修改 dev:web 时不要自动改后端目标策略;dev:web 只负责主站 Vite 端口可用性与已有后端目标选择。
  6. 同步更新技术文档和团队共享记忆。
  7. 如果修改 Linux 端口段注册口径,确认 Windows 分支仍保持旧行为,不要把系统级注册表逻辑扩散到 Windows。

测试与验证

最小验证:

node --check scripts/dev.mjs
npm run test -- scripts/dev-stack-port-utils.test.ts
npm run check:encoding
node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 api:127.0.0.1:0 web:0.0.0.0:0 adminWeb:127.0.0.1:0 bgfilterWorker:127.0.0.1:0

端口冲突回归测试建议:

  1. 用测试或临时 Node server 占用某个优先端口。
  2. 调用 findAvailablePort,断言结果大于被占用端口。
  3. 调用 resolveDevStackPorts,断言五个结果互不相同。
  4. 如果实际启动完整栈,观察控制台:
    • [dev:ports] ... 不可用,改用 ...
    • [dev] api-server: http://...:<actual-api-port>
    • [dev] spacetime: http://...:<actual-spacetime-port>
    • 主站和后台 Vite 启动端口与日志一致。

完整启动属于长驻进程。需要 smoke 时用 background 方式启动,并另开命令检查 api-server /healthz、BgFilter worker /readyz、SpacetimeDB /v1/ping 和两个页面端口;不要等待 npm run dev 自然退出。检查地址必须取 .app/dev-stack.json 或启动日志中的实际端口,不能假定 worker 一定停在 8083

常见坑

  1. 只让 Vite 自己漂移端口。 这样终端可能出现可访问前端,但脚本和文档仍认为是 3000,后台目标或日志会错。
  2. 只改 SpacetimeDB start,不改 publish。 standalone 可能监听新端口,但 publish 仍连旧 3101
  3. 只改 GENARRATIVE_API_PORT,不改 RUST_SERVER_TARGET api-server 已在新端口监听,但 Vite 代理仍打旧端口。
  4. 使用 0.0.0.0 作为浏览器访问地址。 监听可以是 0.0.0.0,展示给用户和健康检查通常用 127.0.0.1
  5. 端口探测和实际启动之间存在竞态。 已经探测可用的端口仍可能被外部进程抢占;SpacetimeDB 启动后仍要解析实际监听地址,api-server 和 Vite 失败时要打印清晰日志。
  6. 运行全仓库 lint 误判。 当前仓库可能有既有 lint 问题。验证本功能时优先运行定向测试、Bash 语法检查、编码检查,并在最终说明中区分既有 lint 失败与本次改动。

验收清单

  • 端口工具有测试覆盖端口被占用和多端口互斥解析。
  • Linux 注册表分配、同用户复用固定段并继续漂移、自动分配从 10000-10099 起、Windows bypass 都有测试覆盖。
  • scripts/dev.mjs 通过 node --check
  • npm run dev 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
  • BgFilter worker 在 api-server 前 ready,父子共享实际 base URL / TokenRust watch 只触发一次组合重启。
  • npm run dev:web 在主站端口不可用时能切换到可用端口。
  • npm run agc 在 Linux 使用用户段 start + 5Tauri、Vite、marker 和预检使用同一最终端口。
  • 文档同步更新 docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
  • 长期踩坑同步更新 docs/project-memory/shared-memory/pitfalls.md
  • 修改中文文件后运行 npm run check:encoding