--- name: genarrative-dev-stack-port-routing short_description: 修改 Genarrative 本地 dev 启动端口、代理目标、端口冲突处理时使用。 description: 在 Genarrative 中修改 npm run dev / npm run dev:rust / npm run dev:web 的本地启动端口、端口可用性探测、端口漂移、SpacetimeDB publish server、api-server 环境变量、Vite 代理目标和后台 admin-web 启动串联时使用。 version: 1.0.0 author: Hermes Agent license: MIT metadata: hermes: tags: [Genarrative, dev-stack, 端口探测, Vite, api-server, SpacetimeDB, npm-run-dev] related_skills: [genarrative-admin-backoffice] --- # Genarrative 本地 dev 启动端口与代理目标串联流程 用于维护 Genarrative 本地开发栈启动脚本,重点覆盖 `npm run dev` / `npm run dev:rust` / `npm run dev:web` 的端口检查、端口漂移和后续流程目标传递。 ## 适用场景 - 修改 `scripts/dev-rust-stack.sh`、`scripts/dev-web-rust.mjs`、`scripts/dev-stack-port-utils.mjs`。 - 处理 `3000`、`3101`、`3102`、`8082` 等端口被占用导致本地开发栈启动失败。 - 排查 Vite 代理仍指向旧 api-server 端口、前端打开了旧 dev server、后台代理错配。 - 调整 SpacetimeDB standalone、publish、Rust `api-server`、主站 Vite、后台 Vite 的启动顺序。 - 修改本地联调文档或 `.hermes/shared-memory/pitfalls.md` 中的 dev 启动口径。 ## 当前端口职责 默认优先端口: 1. 主站 Vite:`3000`,对浏览器通常展示为 `http://127.0.0.1:/`。 2. Rust `api-server`:`8082`,健康检查为 `http://127.0.0.1:/healthz`。 3. SpacetimeDB standalone:`3101`,健康检查为 `http://127.0.0.1:/v1/ping`。 4. 后台 Vite:`3102`,后台地址为 `http://127.0.0.1:/admin/`。 端口不可用时,脚本会从优先端口开始向后寻找可用端口。后续流程必须以解析后的实际端口为准,不能继续使用默认端口。 ## 实现入口 - `package.json` - `dev` 和 `dev:rust`:执行 `node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh`。 - `dev:web`:执行 `node scripts/dev-web-rust.mjs`。 - `scripts/dev-stack-port-utils.mjs` - `isPortAvailable(...)`:探测端口是否可监听。 - `findAvailablePort(...)`:从优先端口向后寻找可用端口,`0` 表示申请临时端口。 - `resolveDevStackPorts(...)`:一次性解析 SpacetimeDB、api-server、主站 Vite、后台 Vite 端口,并避免本次解析结果互相冲突。 - 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`。 - `scripts/dev-rust-stack.sh` - 解析 CLI 参数后,先计算 `API_TARGET_HOST` 与 `ADMIN_WEB_TARGET_HOST`。 - 调用 `resolve_dev_stack_ports` 覆盖 `SPACETIME_PORT`、`API_PORT`、`WEB_PORT`、`ADMIN_WEB_PORT`。 - 再构造 `SPACETIME_SERVER` 和 `RUST_SERVER_TARGET`。 - `scripts/dev-web-rust.mjs` - 单独启动主站前端时,也先用 `findAvailablePort` 检查 `WEB_PORT` / 默认 `3000`。 ## 必须保持的传递链路 `npm run dev` / `npm run dev:rust` 中端口解析后,必须同步到以下位置: 1. SpacetimeDB 启动:`spacetime start --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"`。 2. SpacetimeDB 发布:`spacetime publish ... --server "${SPACETIME_SERVER}"`。 3. Rust api-server:`GENARRATIVE_API_HOST`、`GENARRATIVE_API_PORT`、`GENARRATIVE_SPACETIME_SERVER_URL`、`GENARRATIVE_SPACETIME_DATABASE`。 4. api-server 健康检查:`wait_for_api_server "${RUST_SERVER_TARGET}/healthz" ...`。 5. 主站 Vite:`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET`、`ADMIN_WEB_TARGET`、`ADMIN_WEB_PORT`、`--port=${WEB_PORT}`、`--host=${WEB_HOST}`。 6. 后台 Vite:`ADMIN_API_TARGET`、`GENARRATIVE_API_TARGET`、`GENARRATIVE_API_PORT`、`--port=${ADMIN_WEB_PORT}`。 7. 控制台日志:`[dev:ports]` 和 `[dev:rust] web/admin web/rust api/spacetime` 必须显示最终实际地址。 如果只改了其中一段,通常会出现:浏览器打开的前端可用,但 `/api/*` 代理到旧端口;后台页面可用但后台 API 失败;SpacetimeDB 启动在新端口但 publish 仍发往旧端口。 ## 修改流程 1. 先读当前脚本和文档: - `scripts/dev-stack-port-utils.mjs` - `scripts/dev-rust-stack.sh` - `scripts/dev-web-rust.mjs` - `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md` - `.hermes/shared-memory/pitfalls.md` 2. 优先改公共端口工具,不要把端口探测逻辑复制到多个脚本。 3. 对 Bash 脚本只做局部补丁,避免整文件重写导致中文注释或换行大面积变化。 4. 修改 `dev-rust-stack.sh` 时确认变量顺序: - 先有 `REPO_ROOT`。 - 再计算 `API_TARGET_HOST` / `ADMIN_WEB_TARGET_HOST`。 - 再调用端口解析工具。 - 再构造 `SPACETIME_SERVER` / `RUST_SERVER_TARGET`。 5. 修改 `dev:web` 时不要自动改后端目标策略;`dev:web` 只负责主站 Vite 端口可用性与已有后端目标选择。 6. 同步更新技术文档和团队共享记忆。 ## 测试与验证 最小验证: ```bash bash -n scripts/dev-rust-stack.sh 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 ``` 端口冲突回归测试建议: 1. 用测试或临时 Node server 占用某个优先端口。 2. 调用 `findAvailablePort`,断言结果大于被占用端口。 3. 调用 `resolveDevStackPorts`,断言四个结果互不相同。 4. 如果实际启动完整栈,观察控制台: - `[dev:ports] ... 不可用,改用 ...` - `[dev:rust] rust api: http://...:` - `[dev:rust] spacetime: http://...:` - 主站和后台 Vite 启动端口与日志一致。 完整启动属于长驻进程。需要 smoke 时用 background 方式启动,并另开命令检查 `/healthz`、`/v1/ping` 和页面端口;不要等待 `npm run dev` 自然退出。 ## 常见坑 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 失败与本次改动。 ## 验收清单 - [ ] 端口工具有测试覆盖端口被占用和多端口互斥解析。 - [ ] `dev-rust-stack.sh` 通过 `bash -n`。 - [ ] `npm run dev` / `npm run dev:rust` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。 - [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。 - [ ] 文档同步更新 `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`。 - [ ] 长期踩坑同步更新 `.hermes/shared-memory/pitfalls.md`。 - [ ] 修改中文文件后运行 `npm run check:encoding`。