19 KiB
QQBot NapCat 源码 Fork 二维码刷新修复实施计划(中文)
给执行代理的要求: 实施时必须使用
superpowers:subagent-driven-development(推荐)或superpowers:executing-plans,按任务逐项执行并用复选框追踪。英文计划2026-06-23-qqbot-napcat-source-fork-qr-refresh-implementation-plan.md是精确执行版,包含完整测试代码和源码片段;本文件是同任务、同顺序、同验收标准的中文审核版,供用户确认范围和节奏。
目标: fork NapCatQQ 源码,在源代码层修复 WebUI 登录态 stale 导致二维码无法刷新的问题,并通过 KT desktop-cn-v3 派生镜像上线验证。
架构: 先在 NapCatQQ fork 内修复登录态模型和 QR refresh 可观测性,再让 KT API 的中文桌面镜像消费源码构建产物,替换原来的 bundled JS 字符串补丁。API 侧继续保留旧二维码防护,作为 NapCat/QQ 内核异常时的第二道保护。
技术栈: NapCatQQ monorepo、TypeScript、Vitest、Vite Shell build、NestJS API、Jest、Docker、NAS SSH、Jenkins/K8s。
文件结构
NapCatQQ Fork
目标仓库:D:\MyFiles\KT\GitHub\NapCatQQ
- 修改
packages/napcat-webui-backend/src/types/index.ts- 增加登录运行态、QR revision、QR refresh 结果类型。
- 修改
packages/napcat-webui-backend/src/helper/Data.ts- 登录运行态真相源:负责
QQLoginStatus、selfInfo.online、二维码缓存、revision、stale reconcile。
- 登录运行态真相源:负责
- 修改
packages/napcat-webui-backend/src/api/QQLogin.ts- 所有登录 HTTP handler 改用统一真实在线态判断。
- 修改
packages/napcat-shell/base.ts- Shell 模式 QR refresh callback 返回
loginService.getQRCodePicture()的 accepted 状态。
- Shell 模式 QR refresh callback 返回
- 修改
packages/napcat-framework/napcat.ts- Framework 模式 QR refresh callback 同步返回 accepted 状态。
- 修改
packages/napcat-test/vitest.config.ts- 增加 WebUI backend 测试 alias。
- 新增
packages/napcat-test/webuiLoginRuntime.test.ts- 覆盖 stale 登录态 reconcile、QR revision、QR refresh accepted/updated。
- 新增
packages/napcat-test/webuiQQLoginHandlers.test.ts- 覆盖 stale 登录态下 handler 不再返回
QQ Is Logined。
- 覆盖 stale 登录态下 handler 不再返回
- 新增
packages/napcat-test/webuiLoginSourceWiring.test.ts- 静态检查 Shell/Framework callback 是否返回内核 accepted 状态。
KT API 仓库
目标仓库:D:\MyFiles\KT\Node\kt-template-online-api
- 新增
scripts/napcat-desktop-cn-stage-build.mjs- 把 NapCatQQ fork 的 Shell dist staged 成 Docker build context。
- 修改
ci/napcat-desktop-cn/Dockerfile- 从 staged context 复制源码构建产物,生成
/app/NapCat.Shell.zip。
- 从 staged context 复制源码构建产物,生成
- 修改
ci/napcat-desktop-cn/verify.sh- 校验 fork marker、artifact hash、locale、timezone、XDG、fontconfig 和源码修复 marker。
- 删除
ci/napcat-desktop-cn/patches/qq-login-real-online-guard.sh- 不再做 bundled JS 字符串 patch。
- 修改
ci/napcat-desktop-cn/README.md- 写清 v3 staging/build/verify 命令。
- 修改
src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts- 默认 desktop profile 升级到
desktop-cn-v3。
- 默认 desktop profile 升级到
- 修改
test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts- 旧 patch 断言改成源码 artifact 断言。
- 修改
test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts- 更新默认 runtime profile 版本断言。
- 修改
README.md、API.md- 记录 v3 镜像和
QQBOT_NAPCAT_IMAGE要求。
- 记录 v3 镜像和
KT Root 文档
目标仓库:D:\MyFiles\KT
- 修改
docs/qqbot-nas-runtime.md- 记录 NapCat 源码 fork 镜像、canary 证据和上线边界。
- 修改
TASKS.md- 增加本轮双语计划和后续实施上下文。
执行规则
- 不创建
.worktree,按用户要求使用普通 dev 分支。 - canary 任务之前不改生产容器。
- 不删除 API 侧旧二维码防护。
- 不推送,除非用户明确要求。
- KT 自有新增/触达函数必须有 JSDoc,参数说明要写用途,不复读变量名。
- 每个代码任务必须先跑 RED,再实现,再跑 GREEN。
Task 1:准备 NapCatQQ Fork 分支
文件:
-
创建或更新
D:\MyFiles\KT\GitHub\NapCatQQ -
拉取或克隆上游
NapNeko/NapCatQQ。 -
从
main创建codex/qr-refresh-login-state。 -
确认基线 commit 当前为
5c18a62530d87dbadf53d267002894faa6ca7e90;如上游前进,则记录实际 commit。 -
确认
package.json有typecheck、test、build:shell。
验证命令:
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' rev-parse HEAD
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --version
预期:分支存在,脚本入口可用。
Task 2:新增 NapCat 登录运行态失败测试
文件:
-
修改
D:\MyFiles\KT\GitHub\NapCatQQ\packages\napcat-test\vitest.config.ts -
新增
D:\MyFiles\KT\GitHub\NapCatQQ\packages\napcat-test\webuiLoginRuntime.test.ts -
在 Vitest config 增加
napcat-webui-backendalias。 -
新增运行态测试,覆盖:
QQLoginStatus=true且selfInfo.online=false时 reconcile 为离线。selfInfo.online=undefined时不误判离线。- QR URL 变化时 revision 增加,重复写同 URL 不增加。
- refresh callback 返回
true且 QR callback 更新 URL 时,结果是accepted=true, updated=true。 - refresh callback 返回
false时,结果是 rejected。
-
运行 RED 测试。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiLoginRuntime.test.ts
预期:第一次失败,原因是 __resetForTest、getQQLoginRuntimeState、clearQQLoginQrcodeURL 和带返回值的 refreshQRCode 还不存在。
Task 3:实现 NapCat WebUI 登录运行态模型
文件:
-
修改
packages/napcat-webui-backend/src/types/index.ts -
修改
packages/napcat-webui-backend/src/helper/Data.ts -
测试
packages/napcat-test/webuiLoginRuntime.test.ts -
增加类型:
QQLoginRuntimeStateOptionsQQLoginRuntimeStateQQRefreshQRCodeOptionsQQRefreshQRCodeResult
-
LoginRuntimeType增加:QQQRCodeRevisionQQQRCodeUpdatedAtonRefreshQRCode: () => Promise<boolean>
-
Data.ts增加 helper:delaygetCoreOnlineStatebuildQQLoginRuntimeStateclearQQLoginQrcodeURL
-
setQQLoginQrcodeURL只在 URL 变化时递增 revision。 -
getQQLoginRuntimeState({ reconcile, clearStaleQRCode })统一表达真实在线态。 -
refreshQRCode(options)返回accepted、updated、qrcodeRevision、qrcodeurl。 -
增加
__resetForTest()测试辅助。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiLoginRuntime.test.ts
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-webui-backend run typecheck
预期:测试和 typecheck 通过。
提交:
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' add packages/napcat-webui-backend/src/types/index.ts packages/napcat-webui-backend/src/helper/Data.ts packages/napcat-test/vitest.config.ts packages/napcat-test/webuiLoginRuntime.test.ts
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' commit -m "fix: 修复WebUI登录态与二维码刷新状态"
Task 4:修复 QQLogin handler 登录态短路
文件:
-
新增
packages/napcat-test/webuiQQLoginHandlers.test.ts -
修改
packages/napcat-webui-backend/src/api/QQLogin.ts -
新增 handler 测试,覆盖:
CheckLoginStatus会清理 stale login state,不返回旧 QR。RefreshQRcode在 stale login state 下允许执行。- core 明确在线时 quick login 仍返回
QQ Is Logined。 - stale 后
GetQRcode不返回旧二维码。
-
运行 RED 测试。
-
在
QQLogin.ts增加 helper:isActuallyLoggedIn(clearStaleQRCode = true)buildLoginStatusPayload()
-
将以下 handler 的直接
getQQLoginStatus()判断改为真实在线态判断:QQGetQRcodeHandlerQQSetQuickLoginHandlerQQRefreshQRcodeHandlerQQPasswordLoginHandlerQQCaptchaLoginHandlerQQNewDeviceLoginHandler
-
QQCheckLoginStatusHandler统一返回 helper payload。 -
QQRefreshQRcodeHandler返回 refresh result;若 rejected,则返回明确错误。 -
给触达的 exported handler 补 JSDoc。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiQQLoginHandlers.test.ts
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiLoginRuntime.test.ts webuiQQLoginHandlers.test.ts
预期:测试通过。
提交:
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' add packages/napcat-webui-backend/src/api/QQLogin.ts packages/napcat-test/webuiQQLoginHandlers.test.ts
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' commit -m "fix: 收敛QQ登录接口真实在线态判断"
Task 5:打通 Shell/Framework QR refresh callback 返回值
文件:
-
新增
packages/napcat-test/webuiLoginSourceWiring.test.ts -
修改
packages/napcat-shell/base.ts -
修改
packages/napcat-framework/napcat.ts -
新增静态源码测试,确认 Shell/Framework 都包含
return loginService.getQRCodePicture();。 -
运行 RED 测试。
-
Shell 模式的
setRefreshQRCodeCallback返回loginService.getQRCodePicture()。 -
Framework 模式同样返回
loginService.getQRCodePicture()。 -
运行源码 wiring 测试和两包 typecheck。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiLoginSourceWiring.test.ts
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-shell run typecheck
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-framework run typecheck
预期:测试和 typecheck 通过。
提交:
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' add packages/napcat-shell/base.ts packages/napcat-framework/napcat.ts packages/napcat-test/webuiLoginSourceWiring.test.ts
git -C 'D:\MyFiles\KT\GitHub\NapCatQQ' commit -m "fix: 返回二维码刷新请求接收状态"
Task 6:构建并验证 NapCatQQ Fork Artifact
文件:
-
无新增文件;除非验证暴露明确编译或测试问题。
-
安装依赖。
-
跑新增 WebUI 相关 Vitest。
-
跑 NapCatQQ 现有测试。
-
跑 monorepo typecheck。
-
构建 WebUI frontend。
-
构建 Shell artifact。
-
记录 fork commit 与
napcat.mjsSHA256。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' install --frozen-lockfile
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run webuiLoginRuntime.test.ts webuiQQLoginHandlers.test.ts webuiLoginSourceWiring.test.ts
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-test exec vitest run
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' run typecheck
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' --filter napcat-webui-frontend run build
corepack pnpm --dir 'D:\MyFiles\KT\GitHub\NapCatQQ' run build:shell
Get-FileHash -Algorithm SHA256 'D:\MyFiles\KT\GitHub\NapCatQQ\packages\napcat-shell\dist\napcat.mjs'
预期:packages/napcat-shell/dist/napcat.mjs 存在,所有测试/typecheck/build 通过。
Task 7:新增 API 镜像 build-context staging 脚本
文件:
-
新增
scripts/napcat-desktop-cn-stage-build.mjs -
修改
test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts -
在 API image test 中新增 staging script 静态断言,覆盖:
napcatMjsSha256forkCommitupstreamBaseCommitpackages/napcat-shell/distfork-artifact.json.kt-workspace/napcat-desktop-cn-build
-
运行 RED Jest。
-
新增 staging 脚本:
- 参数
--napcat-root - 参数
--out - 复制
Dockerfile、verify.sh、NapCat.Shelldist - 写入
fork-artifact.json - 计算 dist 与
napcat.mjsSHA256 - 输出 marker 和 output root
- 参数
-
跑 Jest,确认新测试通过;旧 patch 测试会在 Task 8 更新。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' exec jest test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts --runTestsByPath --runInBand
预期:新增 staging 断言通过。
Task 8:用源码 artifact 替换 bundled JS patch
文件:
-
修改
ci/napcat-desktop-cn/Dockerfile -
修改
ci/napcat-desktop-cn/verify.sh -
删除
ci/napcat-desktop-cn/patches/qq-login-real-online-guard.sh -
修改
test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts -
把旧 patch 断言替换为源码 artifact 断言:
- Dockerfile 包含
COPY NapCat.Shell /tmp/NapCat.Shell - Dockerfile 包含
fork-artifact.json - Dockerfile 仍生成
/app/NapCat.Shell.zip - Dockerfile 不再包含
qq-login-real-online-guard.sh - verify 包含
napcatMjsSha256 - verify 包含
getQQLoginRuntimeState - verify 包含
qrcodeRevision
- Dockerfile 包含
-
运行 RED Jest。
-
Dockerfile 删除 patch block,改为复制 staged Shell dist 并 zip 到
/app/NapCat.Shell.zip。 -
verify.sh 校验:
- locale/timezone/font/XDG
/.dockerenv隐藏与 cgroup evidence- fork marker 存在
NapCat.Shell.zip存在- 解压后
napcat.mjshash 与 marker 一致 - bundle 内存在
getQQLoginRuntimeState与qrcodeRevision
-
删除旧 patch 脚本。
-
跑 image Jest。
验证命令:
git -C 'D:\MyFiles\KT\Node\kt-template-online-api' rm -- 'ci/napcat-desktop-cn/patches/qq-login-real-online-guard.sh'
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' exec jest test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts --runTestsByPath --runInBand
预期:Jest 通过,旧 patch 脚本被删除。
Task 9:更新 Runtime Profile 版本与文档
文件:
-
修改
src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts -
修改
test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts -
修改
ci/napcat-desktop-cn/README.md -
修改
README.md -
修改
API.md -
修改
D:\MyFiles\KT\docs\qqbot-nas-runtime.md -
将测试预期从
desktop-cn-v2改为desktop-cn-v3,先跑 RED。 -
将 service 默认值改为
desktop-cn-v3。 -
README 写清:
- 先构建 NapCatQQ fork。
- 再运行 staging 脚本。
- 再 build
kt-napcat-desktop-cn:desktop-cn-v3。 - 再执行容器内
verify.sh。
-
API/README/root docs 写明:生产
QQBOT_NAPCAT_IMAGE应指向验证过的 v3 digest。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' exec jest test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts --runTestsByPath --runInBand --testNamePattern "resolves Chinese Desktop Runtime defaults"
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' exec jest test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts --runTestsByPath --runInBand
预期:RED 后 GREEN,profile 默认版本为 desktop-cn-v3。
Task 10:验证 API 镜像集成并提交
文件:
-
Task 7 到 Task 9 的所有 API/root 改动。
-
跑 API 聚焦 Jest。
-
跑 API typecheck。
-
跑 API/root diff check。
-
跑 global-review。
-
API 仓库提交。
-
根仓库提交 docs/TASKS。
验证命令:
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' exec jest test/modules/qqbot/napcat/napcat-desktop-cn-image.spec.ts test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts --runTestsByPath --runInBand
corepack pnpm --dir 'D:\MyFiles\KT\Node\kt-template-online-api' run typecheck
git -C 'D:\MyFiles\KT\Node\kt-template-online-api' diff --check
git -C 'D:\MyFiles\KT' diff --check -- TASKS.md docs/qqbot-nas-runtime.md
pnpm --dir 'D:\MyFiles\KT\mcp\ktWorkflow' run global-review
预期:全部通过,global-review findings=[]。
提交命令:
git -C 'D:\MyFiles\KT\Node\kt-template-online-api' add ci/napcat-desktop-cn scripts/napcat-desktop-cn-stage-build.mjs src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts test/modules/qqbot/napcat README.md API.md
git -C 'D:\MyFiles\KT\Node\kt-template-online-api' commit -m "feat: 接入NapCat源码构建镜像"
git -C 'D:\MyFiles\KT' add docs/qqbot-nas-runtime.md TASKS.md
git -C 'D:\MyFiles\KT' commit -m "docs: 记录NapCat源码构建运行态"
Task 11:构建镜像并执行本地或 NAS verify
文件:
-
无源码文件;只有验证证据。
-
从 API 仓库运行 staging 脚本。
-
用 pinned base image 构建
kt-napcat-desktop-cn:desktop-cn-v3。 -
启动一次 verify 容器。
-
容器内执行
/ci/napcat-desktop-cn/verify.sh。 -
删除 verify 容器。
-
记录 image id/digest。
验证命令:
node scripts/napcat-desktop-cn-stage-build.mjs `
--napcat-root D:\MyFiles\KT\GitHub\NapCatQQ `
--out .kt-workspace\napcat-desktop-cn-build
$baseImage = docker image inspect mlikiowa/napcat-docker:latest --format '{{index .RepoDigests 0}}'
if (-not $baseImage) { docker pull mlikiowa/napcat-docker:latest; $baseImage = docker image inspect mlikiowa/napcat-docker:latest --format '{{index .RepoDigests 0}}' }
docker build `
--build-arg NAPCAT_BASE_IMAGE=$baseImage `
-t kt-napcat-desktop-cn:desktop-cn-v3 `
-f .kt-workspace/napcat-desktop-cn-build/ci/napcat-desktop-cn/Dockerfile `
.kt-workspace/napcat-desktop-cn-build
$name = "kt-napcat-v3-verify-$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())"
docker run -d --name $name kt-napcat-desktop-cn:desktop-cn-v3
docker exec $name sh /ci/napcat-desktop-cn/verify.sh
docker rm -f $name
docker image inspect kt-napcat-desktop-cn:desktop-cn-v3 --format '{{.Id}}'
预期:镜像构建成功,verify 退出码为 0。
Task 12:部署并进行线上 canary
文件:
-
无源码文件;除非线上验证暴露明确 bug。
-
用户确认后再 push。
-
push NapCatQQ fork 分支。
-
push API main。
-
观察 Jenkins/K8s。
-
配置生产环境:
QQBOT_NAPCAT_IMAGE=kt-napcat-desktop-cn:desktop-cn-v3QQBOT_NAPCAT_DESKTOP_PROFILE_VERSION=desktop-cn-v3
-
只对一个用户确认账号做 canary。
-
捕获完整证据:
- NapCat fork commit
- NapCat image id
- scan session id
- old QR hash prefix
- new QR hash prefix
- qrcode.png old/new mtime
RefreshQRcode.acceptedRefreshQRcode.updated- SSE 最终状态
部署观测命令:
pnpm --dir 'D:\MyFiles\KT\mcp\ktWorkflow' run deploy-observation -- --project api --job KT-Template/KT-Template-API/main --execute
canary 通过标准:
- stale
QQLoginStatus=true + online=false不再返回QQ Is Logined。 - NapCat 生成新 QR,或返回
accepted=true, updated=false且 API 不展示旧码。 /app/napcat/cache/qrcode.pnghash/mtime 能证明是否真的产码。- API
scan/status不返回旧 QR hash。 - Admin/SSE 显示新 QR 或明确 pending 原因。
自检清单
- 源码 fork:Task 1-6 覆盖。
- stale 登录态修复:Task 2-4 覆盖。
- QR refresh 可观测:Task 2、3、5、12 覆盖。
- WebUI handler 不回旧码:Task 4 覆盖。
- API 防旧码保留:Task 7-10 不改 API 登录 service。
- 源码 artifact 镜像:Task 7-11 覆盖。
- 本地与线上验证:Task 6、10、11、12 覆盖。
- 本中文计划已完成自检。