确认 goosed-prod-1..9 映射 18006-18014,更新 topology、发包必看、 发布 skill 与 release 脚本的动态 pool 校验。 Co-authored-by: Cursor <cursoragent@cursor.com>
9.0 KiB
发包必看
适用范围:
test-memindPortal runtime 发布、goosed-prod镜像更新、103/105 生产链路排障。
这份文档是发布前强制阅读的事故经验清单。后续发包如果绕过这里,容易把 2026-06-27 的问题重新带回生产。
0. 先确认边界
- 生产 Portal 主机是
103 / Studio:john@58.38.22.103,默认 SSH 命令不要再写旧端口2222。 105是云侧入口/历史链路,不是 Portal 源码真相;不要 SSH 到 105 直接改业务代码。- Portal 生产目录:
/Users/john/Project/Memind。 - goosed 生产目录:
/Users/john/Project/goosed-prod。 - Portal 生产必须是无源码 runtime 模式,只能用
scripts/release-portal-runtime-prod.sh发包。 - 禁止直接
rsync到 103/105,禁止在 103 上npm install、npm run build后继续跑。
1. 发包前硬性检查
- 当前代码必须已经包含以下修复,或位于它们之后:
fc5b50c fix: make goosed mcp paths container safe0420c42 fix: make schema bootstrap mysql safe
- release worktree 必须干净:
git status --short
- 不允许把
.runtime/portal/、node_modules、本地构建缓存提交进 Git。 - 如果 release worktree 需要临时复用主 worktree 的依赖,只能临时建 symlink,发布/测试后必须删除:
ln -s /Users/john/PycharmProjects/test/test-memind/node_modules /Users/john/PycharmProjects/test/test-memind-release-goosed-mcp/node_modules
# 测试或发布完成后:
rm -f /Users/john/PycharmProjects/test/test-memind-release-goosed-mcp/node_modules
- 发包前至少跑:
node --test db.test.mjs capabilities.test.mjs llm-providers.test.mjs wechat-mp.test.mjs
npm run check:mindspace-public-links
public/*.html里的下载/附件相对链接(如report.docx)必须在同目录真实存在。发版脚本默认只查这类链接(--downloads-only);全量资源检查用npm run check:mindspace-public-links:all。失败会阻断发版;确知要带着已知坏链上线时,才可临时ALLOW_MINDSPACE_PUBLIC_LINK_ISSUES=1。
2. Portal runtime 发布唯一流程
只使用:
bash scripts/release-portal-runtime-prod.sh --skip-tests --yes
这个脚本必须完成这些动作:
- 本机构建
.runtime/portal。 - 上传 artifact 到 103。
- 103 备份当前
/Users/john/Project/Memind全目录。 - 103 单独备份持久目录:
.env、MindSpace/、data/、users/、.tailscale/、public/plaza-covers/、logs/。 - 原子切换 live 目录。
- 更新 LaunchAgent 指向
/Users/john/Project/Memind/scripts/run-memind-portal-prod.sh。 - 健康检查
http://127.0.0.1:8081/api/status返回 200。 - 校验
MindSpace/*/public/*.html下载/附件相对链接均已落盘(check-mindspace-public-links.mjs --downloads-only)。
发布成功后,记录脚本输出里的:
release_idarchived_sourcefull_backuppersist_backupgit_head
3. Portal 发布后必须验收
在本机执行:
curl -k -i https://m.tkmind.cn/auth/login \
-H 'content-type: application/json' \
--data '{"username":"john","password":"wrong-password"}' | head -40
正确结果应该是 401,消息类似 用户名或密码错误。
如果返回 503 且消息是 未配置用户数据库或访问密码,说明用户系统 bootstrap 失败,不是简单健康检查能发现的问题。
在 103 执行:
ssh john@58.38.22.103 '
printf "portal="; curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/api/status
for p in $(seq 18006 18014); do
printf "goosed_${p}="
curl -k -s -o /dev/null -w "%{http_code}\n" https://127.0.0.1:${p}/status
done
cd /Users/john/Project/Memind && node scripts/check-mindspace-public-links.mjs --root MindSpace --downloads-only
pid=$(pgrep -f "node .*server.mjs" | head -1)
echo "portal_pid=${pid}"
ps eww -p "$pid" | tr " " "\n" | awk -F= "/^(DATABASE_URL|H5_PORT|TKMIND_API_TARGETS|GOOSED_MCP_NODE_PATH|GOOSED_MCP_SERVER_PATH)=/ {print \$1\"=SET\"}"
'
必须看到:
portal=200goosed_18006=200…goosed_18014=200(共 9 个 target)DATABASE_URL=SETTKMIND_API_TARGETS=SETGOOSED_MCP_NODE_PATH=SETGOOSED_MCP_SERVER_PATH=SET
日志检查:
ssh john@58.38.22.103 '
tail -200 ~/Library/Logs/memind-portal.log |
egrep "User auth bootstrap failed|ER_PARSE_ERROR|Cannot find package|Failed to add extension|No such file|Unknown extension|Provider not set|not configured" || true
'
新启动之后不能继续出现上述错误。
4. 2026-06-27 事故复盘形成的禁止项
4.1 不要只看 /api/status
/api/status 返回 200 只能说明 Express 进程活着,不能说明登录、数据库、Agent 会话策略都正常。每次 Portal 发包必须额外测 /auth/login。
4.2 schema 必须兼容 MySQL
禁止在 schema.sql 里写 PostgreSQL partial index:
KEY idx_xxx (...) WHERE ...
MySQL 不支持这种写法。生产曾因此触发:
User auth bootstrap failed
ER_PARSE_ERROR
未配置用户数据库或访问密码
db.mjs 现在使用 splitSqlStatements(),会跳过 SQL 注释里的分号;不要改回 sql.split(';')。
4.3 不要把本地 node_modules symlink 打进包
release worktree 如果有 node_modules -> /Users/john/.../node_modules symlink,构建脚本必须 realpath 后复制真实依赖。发布后必须确认线上:
ssh john@58.38.22.103 'cd /Users/john/Project/Memind && test -d node_modules/http-proxy-middleware && echo ok'
否则可能出现:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'http-proxy-middleware'
4.4 Portal 下发给 goosed 的 MCP 路径必须是容器内路径
推荐并默认使用:
GOOSED_MCP_NODE_PATH=/usr/local/bin/node
GOOSED_MCP_SERVER_PATH=/opt/portal/mindspace-sandbox-mcp.mjs
不要让新会话继续依赖宿主机路径:
/opt/homebrew/Cellar/node@24/24.16.0/bin/node
/Users/john/Project/Memind/mindspace-sandbox-mcp.mjs
否则会触发:
Failed to add extension: IO error: No such file or directory (os error 2)
4.5 code_execution 默认不能下发
当前 goosed 不认识 code_execution 扩展。除非确认 goosed 已支持,否则不要设置:
TKMIND_ENABLE_CODE_EXECUTION_EXTENSION=1
4.6 Provider 必须同步到全部 goosed target
Portal 现在使用:
TKMIND_API_TARGETS=https://127.0.0.1:18006,https://127.0.0.1:18007,https://127.0.0.1:18008,https://127.0.0.1:18009,https://127.0.0.1:18010,https://127.0.0.1:18011,https://127.0.0.1:18012,https://127.0.0.1:18013,https://127.0.0.1:18014
发布或重建 goosed 容器后,要确认 18006–18014 全部 target 都有 provider 配置。否则 Agent 会出现:
Provider 'custom_deepseek' is not configured
注意:DeepSeek API key / provider secret 是运行配置,不应该烘焙进镜像。
5. goosed-prod 镜像更新必看
当前已验证镜像:
tkmind/goosed:prod-20260627-2125-mcp
这个镜像必须保证以下路径在容器内存在:
/usr/local/bin/node
/opt/homebrew/bin/node
/opt/homebrew/opt/node@24/bin/node
/opt/homebrew/Cellar/node@24/24.16.0/bin/node
/opt/portal/mindspace-sandbox-mcp.mjs
重建镜像后必须验:
ssh john@58.38.22.103 '
cd /Users/john/Project/goosed-prod
set -a
source .env
source .paths.env
export PORTAL_RUNTIME_DIR="$portal_runtime_dir" MINDSPACE_ROOT="$mindspace_root"
set +a
/opt/homebrew/bin/docker compose -f docker-compose.prod.yml ps
for c in goosed-prod-{1..9}; do
echo "--- $c"
/opt/homebrew/bin/docker exec "$c" sh -lc "
ls -l /usr/local/bin/node \
/opt/homebrew/bin/node \
/opt/homebrew/opt/node@24/bin/node \
/opt/homebrew/Cellar/node@24/24.16.0/bin/node \
/opt/portal/mindspace-sandbox-mcp.mjs
"
done
'
重建容器后必须重新确认:
- 九个容器都是
healthy。 18006–18014/status都是 200。- provider 已同步到全部 target。
- sandbox-fs smoke 能成功
add extension并完成一次/reply。
6. 出问题时先走这条判断链
m.tkmind.cn/api/status是否 200?/auth/login是否进入 400/401,而不是 503?- 103 Portal 进程是否拿到了
DATABASE_URL? ~/Library/Logs/memind-portal.log是否有User auth bootstrap failed?18006–18014是否全 200?- goosed 容器内
/usr/local/bin/node和/opt/portal/mindspace-sandbox-mcp.mjs是否存在? - provider 是否同步到全部 target?
- 如果是微信服务号或专属 Agent 报错,必须测真实业务路径,不要只看健康接口。
7. 发布完成后给用户的最小回报格式
发布完成后至少说明:
- release id
- git head
- full backup
- persisted backup
- Portal 健康结果
/auth/login结果18006–18014健康结果(共 9 个 target)- 是否发现并处理了日志里的错误关键词
不要只说“已发布成功”。