games-development-ai/.agents/skills/ui-walkthrough-cdp.md
lili 3436f735f2 docs(skills): ui-walkthrough §5.6 增「本地 mock 预览渲染真适配器游戏」配方
沉淀 E1↔E6 合龙技法:engineBundle(__GameBundle)经静态 public/ manifest(脚本生成,gitignore)+ runtime mock 回填 manifestUrl+checksum → 宿主 fetchAndVerifyManifest → 引擎包路真玩,绕开 T1b 替换中自研壳 demo 兜底的 load_timeout。记 3 坑(静态路径非 /app-api、checksum 校原文、srcdoc allow-same-origin origin)+ CDP 验法。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:07:37 -07:00

8.4 KiB
Raw Blame History

真 UI 走查配方CDP on mini-desktop

为什么必须真 UI 走查:编排器/批跑/curl 全绿 ≠ 真人 UI 能用。编排器旁路曾掩盖 2 个真后端发布缺陷(currentVersionId 生成后未回写 / getZones 空桩),真 UI 走查是逮这类缺陷的唯一手段HJ-FE-WALK-001。链路①创作→生成→预览与链路②发布→审核→入 feed均用本配方闭合。 工具已入仓 docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/player_cdp.py(游戏试玩 CDP含 BrowserPool/ admin_walk.pyadmin 最小 CDP 客户端new_target/send/evaljs/nav/shot/ run_walk.shchrome 生命周期)/ probe_bridge_channel.py宿主↔iframe 双边信道探针)。

1. 大原则

  • chrome 一律在 mini-desktop 本地起6c6g 连续 exit 144该路线已作废被测前端/后端同机,全 localhost。
  • 浏览器内访问前端优先 http://localhost:4173(安全上下文,省心)。经 Tailscale IP/明文 http 现也可用:宿主 manifest 校验的 crypto.subtle 在非安全上下文缺失,已补纯 JS SHA-256 兜底2026-06-17 4e486f60),不再「走 IP 永远 demo 兜底」。
  • 红线(此坑血泪)上条「IP→demo 兜底」曾被当成「走查改用 localhost 规避」记着,却没意识到创始人/真实用户正是经 IP 访问 → 线上游戏一直「加载失败」demo 退化后 W-CLEAN 占位发 game_error。教训:走查环境 localhost = 安全上下文,会系统性掩盖「上下文依赖缺陷」(crypto.subtleisSecureContext 门控特性等);用户可见 UI/e2e 走查必须另跑一遍真实访问 originIP/明文 http不能只在 localhost 取证。当走查需要一个 workaround 才能避开某失败时,先问「真实用户会不会也撞上同一失败」。
  • chrome 起后必先 curl -s localhost:9222/json/version 验活再用。

2. CDP 七坑Chrome 146 实证,逐个血泪)

  1. root 运行必须 --no-sandbox
  2. --no-zygote 必加(否则 Failed global descriptor lookup → 渲染进程崩 → 空 DOM
  3. WS 握手 403 → 启动加 --remote-allow-origins=*,或 websocket-client 用 suppress_origin=True
  4. Chrome 111+ 建 target 用 PUT /json/new?urlGET 不行);
  5. ssh 直启 chrome 必死(继承 ssh 会话 fd断连即杀nohup/setsid 救不全)——正解=整个流程写成 mini 上的脚本,脚本内 exec >/tmp/walk_out.txt 2>&1 重定向自身输出,nohup bash run_walk.sh & 启动即返回,远端 until-loop 轮询输出文件;
  6. 单文件传 minicat local | ssh mini-desktop 'cat >/tmp/x'
  7. 轮询禁 sleep N && cmd(会被拦),用远端 for i in ...; do grep -q DONE file && break; sleep 2; done

3. studio 走查(创作/试玩/发布链路)

  • 入口 :4173;受控 textarea 填值 = 原型 setter + dispatch input 事件(触发 Vue v-model直接赋值无效
  • 点按钮用精确文案匹配防子串误中(includes('满意') 会误中「不满意,重做」);
  • 截图 cat 管道拉回本地用 Read 查看;游戏试玩复用 player_cdp.play()(批跑必须 continue_on_demo=False)。
  • localStorage 注入 token 过 requiresAuth 走查免登录直达受保护页只读走查——2026-06-16 Lane E G0 实证:只想只读核实一个 meta.requiresAuth 的页面(如创作页),不必走完整登录表单。先后端拿一枚有效 token如免短信登录端点 / test1 token再用 CDP 在目标 origin 下注入 localStorage 后再导航:
    evaljs("localStorage.setItem('ACCESS_TOKEN', '<token>')")   # key 名以前端约定为准(查 utils/auth);有的还需 refreshToken/tenantId
    nav("http://localhost:4173/<受保护路由>")                     # 注入后再导航,路由守卫读 token 即放行
    
    坑:① 必须在同 origin 已开的 target 里 setItemlocalStorage 按 origin 隔离,先 nav:4173 任一页建立 origin 再 set,空白页 set 不生效);② key 名/附加字段以前端真实约定为准(错名=守卫仍判未登录弹回登录页);③ 仅供只读走查,真链路验收(创建→生成→发布)仍要走真登录路径。

4. admin 走查(审核台,链路②)

  • 接 staginggame-admin/.env.local 两行——VITE_BASE_URL=http://<staging>:48080 + VITE_APP_WANXIANG_MOCK=false(不关全是假数据,最易漏)chrome 与 admin 同机时 BASE 可 localhost。
  • 登录:租户「芋道源码」/ admin / admin123表单预填点登录即过无验证码
  • 审核台:静态路由 /wanxiang/review(登录后侧栏「造梦运营→项目审核」恒在);「通过」= POST /admin-api/project/review {decision:1} → 同事务 publish
  • 验收锚点:UI 队列数变化 + DB 双实证game_project.status=4 + game_feed_rank 新行)。

5.5 mock HTTP 快走查lili-macCDP 前置层,省心逮 80% 接线缺陷)

CDP 全走查重chrome 生命周期 + 受控输入 + 截图)。前端接新契约(如 U6 studio.ts先做一层 HTTP 快走查逮「mock 没注册 / vite 中间件没拦 / 字段不贴契约」这类接线缺陷,再上 CDP 验 UI 行为:

  1. npm run build(vue-tsc -b 类型 + vite build)= 类型/编译门;
  2. mock 调度 smoke:npx tsx 直 import src/mock/index.tsdispatch(method,url,body)(DOM-free)断言 create/poll/校验/锁——不起服务器验 mock 逻辑;
  3. mock-over-vite HTTP 快走查:vite preview --port 4173(VITE_API_BASE 空 → mock=ON,configurePreviewServer 也挂中间件)后 curl/app-api/<mod>/*——验整条 HTTP 路径(前端 axios 会走的同一条:vite 中间件→mock handler→契约信封 {code,data,msg});异步任务链轮询到终态(status=2 + 回填 versionId)验「无定时器时间式推进」收敛。
  4. 三层全绿后,UI 行为(Vue 响应式/导航/点击)+ 真实 origin 上下文依赖才上 §3 CDP(权威仍在 mini-desktop 真 origin,localhost 系统性掩盖上下文缺陷见 §1 红线)。
    • 禁裸 sleep 轮询(harness 拦):用计数 for 循环 + curl 自身耗时,或 Monitor until-loop。

口径:HTTP 快走查 = lili-mac 本机省心层(逮接线/契约漂移);权威 UI e2e 仍 = mini-desktop CDP 真 origin。前者绿不等于后者免做(上下文依赖缺陷只在真 origin 现形)。

5.6 让本地 mock 预览渲染【真适配器游戏】(闭合 create→预览可玩闭环)

本地 /create/preview/:versionId 默认走「runtime mock 无 manifestUrl → demo 兜底」,而 demo 兜底用的自研壳正被 T1b(LittleJS+Runner v2)替换 → 本地 load_timeout 玩不了。要让本地预览渲染【真生成物】(E1 适配器产物),把 engineBundle 经静态 manifest 喂给宿主引擎包路:

  1. = 仓内已提交的九门探针 bundle(game-runtime/games/_wg1-gen/_adapter-probe/bundle.iife.js,__GameBundle.bootGameHost 入口)。
  2. 打包 = 脚本(game-studio/scripts/gen-preview-manifest.mjs)把 bundle 包成 GamePackage(engineBundle 内嵌)写 public/mock-manifests/9001.json(gitignore,体积大);打印 OUTER_CHECKSUM = sha256(被服务 json 原文)
  3. 接线 = mock/runtime.ts 对该 versionId 回填 manifestUrl='/mock-manifests/9001.json' + checksum=<OUTER_CHECKSUM>。宿主 GamePlayer.resolvePackagefetchAndVerifyManifest(校 sha256)→ 引擎包路 inject.ts 独立内联 <script> 暴露 window.__GameBundlebootGameHost({canvas,seed})→ 真玩 + 发 game_loaded(不再超时)。
  4. :① manifestUrl 必须是 vite 原样服务的静态路径(public/),不能用 /app-api/*(会被 mock 中间件套 {code,data,msg} 信封,破坏 raw fetch);② checksum 校的是被服务 json 原文的 sha256(非 bundle 自身、非 manifest 内层 checksum),换源 bundle 须重跑脚本同步 runtime.ts 常量;③ srcdoc allow-same-origin → 文档 origin=宿主 origin(非 'null'),已在 bridge 白名单。
  5. :CDP 走查预览页状态 加载失败(load_timeout)可试玩.preview-errtip=null、截图见游戏渲染(实证:tap-target Score:0/3 + 3 目标)。

5. 信道探针(回归排查常备)

宿主↔iframe postMessage 断裂用 probe_bridge_channel.py 取证:四锚点 + 判别实验(文档代际/iframe 重建监视/存活代理注入v-for ref 数组案host→game 全断)即由它锁凶。修复类波次收口前跑一轮防回归。