From 487b46420b06f202b2ffed5476042ce86ec1175b Mon Sep 17 00:00:00 2001 From: yu <1919727607@qq.com> Date: Thu, 5 Nov 2026 17:02:19 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E8=AE=B0=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=97=A5=E5=BF=97(=E9=AA=8C=E8=AF=81=E7=A0=81=E4=B8=93?= =?UTF-8?q?=E9=A1=B9=E4=B8=8E=E5=81=87=E5=BF=85=E5=A1=AB=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=B8=85=E9=9B=B6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .workbuddy/memory/2026-11-05.md | 254 ++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 .workbuddy/memory/2026-11-05.md diff --git a/.workbuddy/memory/2026-11-05.md b/.workbuddy/memory/2026-11-05.md new file mode 100644 index 0000000..079bfa0 --- /dev/null +++ b/.workbuddy/memory/2026-11-05.md @@ -0,0 +1,254 @@ + +## CC Switch 流程「复制失败」修复 +- 根因:流程用 WebBrowser_RunJsInIeBySelector 执行 button.click(),合成点击无 user activation,网页剪贴板 API 被拒 → 页面提示"复制失败",但节点返回 true 看似成功;Clipboard_GetText 读到的是残留旧内容(窗口标题)。 +- 修复:改写 %APPDATA%\rpa-studio\flows\imported_cc_switch_mw2tyjfu_lmq9.json,全流程去 JS: + - loop_click_copy → Element_Click_V6(CDP Input.dispatchMouseEvent 可信点击),选择器 css:table tbody tr:nth-of-type(${index+1}) button[title="复制到剪贴板"],retryCount 2 + - node_read_endpoint(JS) → Element_GetText_V5(css:body, pageText) + 新增 Regex_Find 节点(pattern: API\s*端点[^\n]*?(https?://[^\s|]+),index 1 → keyEndpoint),边已重连 +- 引擎插值确认:runtime 对所有 values 通用 interpolate,${index} 表达式在 uiSelector 中可用。 + +## dev 启动 bufferutil 报错修复 +- 现象:dev 启动弹 "Could not resolve 'bufferutil' imported by 'ws'",主进程崩溃。 +- 根因:package.json 根依赖里 ws 声明丢失(疑似合并冲突解决时被覆盖),externalizeDepsPlugin 默认只外部化 dependencies,ws 被打进 bundle,rollup 为其可选依赖 bufferutil 生成顶层 throw 存根。 +- 修复:package.json dependencies 补 "ws": "^8.18.0" + npm install 同步 lockfile;重启 dev 正常(out/main/index.js 从 793KB 降到 671KB,bundle 内 require("ws") 运行时解析)。 +- 教训:ws 必须保持在根 dependencies,否则 ws 打包坑(存根 throw + permessage-deflate)在 dev/打包都会复发。 + +## CC Switch 流程端点提取修复(第 2 轮) +- 复制修复验证成功:可信点击后剪贴板读到真实密钥 sk-de6855...。 +- Regex_Find 返回空:页面文本 "API 端点" 与 URL 之间隔多个换行,[^\n]*? 不跨行 → 改为 [\s\S]*? 跨行非贪婪,用真实 pageText 自测通过。 +- sw_click_tab 选择器 type=Button 太严 → 去掉类型限定,改为 window=CC Switch;name=Codex。 + +## 流程修改被应用覆盖的坑(重要) +- 现象:直接改 %APPDATA% 下的 flow JSON 后运行,修改被回滚——应用在运行流程时会把内存中的流程副本 flows:save 回磁盘,覆盖外部修改。 +- 正确姿势:改 flow JSON 必须先关闭应用 → 改完回读验证 → 再启动应用(内存副本刷新为磁盘版)。 +- 端点正则最终版:API\s*端点[\s\S]*?(https?://[^\s|]+)(index 1);sw_click_tab 选择器放宽为 window=CC Switch;name=Codex。已落盘+自测通过,应用已重启加载。 + +## 新流程:淘宝 · 手机搜索比价(走真实流程迭代模式) +- 用户反馈:不用手搓 CDP 走查,应用本身有浏览器接管+拾取器+运行记录,按 CC Switch 的模式「建草稿→跑→看 run.json 迭代」即可。 +- 已建 %APPDATA%\rpa-studio\flows\flow_taobao_phone_price.json(18 节点):Edge 打开淘宝→搜手机→等结果(40s 给手动登录/滑块)→分列抓标题/价格/店铺/销量→DataService_LargeModel 比价分析→Bivartable 逐行写入(A-D 列)+AI 结论(F2)。 +- 选择器为猜测值(input[name=q]、button.btn-search、div[class*=doubleCardWrapper]、[class*=title/price/shopName/realSales]),节点标了【拾取确认】,跑挂了用拾取器重采。 + +## 拾取器跟随页面跳转(已提交 5250bf1) +- 用户痛点:目标页面要在运行后才能到达,拾取器只认打开时的页面。 +- 重构 pickElement:拾取脚本改由 Page.addScriptToEvaluateOnNewDocument 随新文档自动重注入,主进程每 400ms 轮询 window.__rpaPickResult;页面跳转不再失效,拾取窗口里可翻页/搜索/过滑块后继续拾取。Esc 取消→cancelled,超时→timeout。 +- 定位根因:早上的 bufferutil 崩溃是合并 2ae1010 把 config 的 include:["ws"] 和 package.json 的 ws 依赖都覆盖丢了,已双双恢复。 +- 教训:electron-vite dev 不会自动重编译主进程改动,改代码后需重启 dev。 + +## 拾取器跟随新标签页(第 2 轮) +- 痛点:淘宝点搜索会开新标签页,拾取附着在旧标签页失效。 +- 方案:pickElement 每 400ms 轮询 /json/list,pages[0](前台标签页)变化时 activateTab 切附着 + Page.enable + 重注册 addScriptToEvaluateOnNewDocument + 重注入;finally 对所有 tabTransports 清残留、移除全部注入。 +- 已提交,typecheck + 25 单测通过,dev 已重启生效。 + +## 拾取器支持钻入子元素(第 3 轮) +- 痛点:淘宝结果整卡是一层 链接,标题/价格是其子元素;层级调整只能向上走祖先链,选不到内部标题。 +- 方案:悬停记录 deepestOf(光标下最深元素),锁定时把「锁定元素→最深元素」的子级路径并入 chain;PageUp 向上升/PageDown 向下钻。空格锁定→PageDown 几次到标题→空格确认。 +- 已提交;typecheck + 25 单测过;dev 已重启。 + +## 拾取器悬停自动钻入(第 4 轮) +- 反馈:PageDown 未锁定时会翻页(要先空格锁定,太隐蔽);用户建议悬停自动钻。 +- 实现:进入新交互区域吸附交互元素,悬停 300ms 自动钻入光标最深元素,钻入后跟随光标在内部移动;PageUp/PageDown 未锁定时直接锁定(PageDown=最深处,PageUp=交互元素),锁定后上下调层。 +- 提交 + dev 重启生效。 + +## 拾取器泛化动态 id(第 5 轮) +- 痛点:淘宝卡片根节点是唯一 id(item_id_1087512368188),cssPath 直接用 #id,选择器只命中一张卡。 +- 方案:stable() 对以 ≥6 位数字结尾的 id,若 [id^="前缀"] 命中 >1 元素则优先泛化采用;普通 id 行为不变。两处 cssPath(pickElement 注入脚本 + elementReferenceHelpers)同步修改。 +- 生成结果示例:[id^="item_id_"] > div:nth-of-type(1) > ... 每张卡都能命中。 +- 提交后 dev 重启生效。 + +## 拾取器残留守卫 bug(第 6 轮,根因级) +- 现象:泛化 id 已提交但用户拾取仍出 #item_id_ 唯一 id。 +- 排查:产物含新代码、真实页面 [id^=item_id_] 有 46 个、正则正确 → 锁定环境问题。 +- 根因:11:13 强杀 electron 时页面挂着未结束的拾取,__rpaPickerActive.active 永久为 true;引导脚本守卫 if(active) return 使新脚本永不生效,一直由旧代码残留监听器响应。 +- 修复:守卫改强制接管(cancelHard+清状态+删宿主),pickElement 注入前同步清残留。 +- 验证:本地复现页两轮 E2E(CDP 合成鼠标+两段式空格:空格一次=锁定、两次=确认),无残留/有残留均输出 css:[id^="item_id_"]...。25 单测过,已提交,dev 重启生效。 +- 环境教训:本机手工拉起的测试 Edge 容易被用户随手关掉,测试要"拉起+跑"一条命令内完成;ECONNREFUSED 不一定是端口死,也可能是竞态。 + +## 泛化阈值时序 bug(第 7 轮,最终修复) +- 现象:全新应用+全新浏览器仍出 #item_id_ 唯一 id;现场检查 9222 实例:46 卡、正则/条件全对。 +- 根因:浏览器 11:45:05 刚拉起用户就拾取,页面渐进渲染只有 1 张卡,泛化条件"命中>1"不成立回退唯一 id。 +- 修复:动态数字 id(≥6 位数字结尾)命中 >=1 即泛化(此时与原 id 等价,零风险)。单卡页面 E2E 验证输出 css:[id^="item_id_"]... +- 已提交,dev 重启生效。拾取器共 7 轮迭代:跳转重注入→跟前台标签页→钻入子元素→悬停自动钻→泛化动态id→强制接管残留→泛化阈值。 + +## 模板字面量吃反斜杠(第 8 轮,真正根因) +- 现象:阈值修复后用户重拾仍出唯一 id。 +- 根因:PICKER_BOOTSTRAP_SOURCE 是 TS 模板字面量,编译时 \\d 被当无效转义吞掉反斜杠,运行时正则变成字面量 d{6,} 永不匹配 → 泛化从未生效。此前 E2E 直接注入源码原文(反斜杠还在)绕过了编译步骤,所以全绿——教训:验证必须用 bundle 里提取的真实运行时串。 +- 修复:正则改 [0-9]{6,}(无转义需求);从 bundle 提取真实脚本重跑 E2E 输出 css:[id^="item_id_"]...,应用已重启生效。 +- 通用教训:模板字符串内的正则永远别用 \\d 类转义,用 [0-9] 或双写反斜杠。 + +## 淘宝流程四列选择器定稿(真实页面验证) +- 用户拾取标题成功(泛化 id 生效)。价格/店铺/销量三个猜的选择器 0 命中:卡片是 不是 div,且新版类名变了。 +- 真实页面探测+验证(46 卡):标题 [id^="item_id_"] > div:nth-of-type(1) > ... > span(46);价格 [id^="item_id_"] [class*="innerPriceWrapper"](46,纯数字);店铺 [class*="shopNameText"](46,注意 shopName 会双匹配 92);销量 [class*="realSales"](31——广告卡无销量,列尾可能少量错位)。 +- 已按"关应用→改流程→重启"写入三个 uiSelector。表头 tableRows、AI 提示词、写入循环均已配好。 + +## 淘宝流程运行前检查 12 错误修复 +- 原因:用户在应用里拾取/编辑后应用保存流程,把我之前补的 web/cssSelector/xpath 必填字段覆盖掉了;且拾取把 node_read_titles 的 xpath 写成了 ${searchReady}。 +- 校验依据:resources/catalog.json 中 Element_SimilarElementList_V2 必填 = uiSelector/designated_obj/web/cssSelector/xpath/parent_selector/attr_name;运行时 selectorOf 默认优先 uiSelector,cssSelector/xpath 仅为校验用。 +- 修复:关应用→四个节点补 web=当前浏览器、cssSelector=uiSelector 去 css: 前缀、xpath=/→回读校验齐备→重启。注意 ${searchReady} 是 node_wait_search 的输出变量(node_input 的 designated_obj 在用),xpath 不能指向它。 + +## AI 提示词字段 + 循环变量警告修复 +- AI 节点错误:DataService_LargeModel 必填字段是 hint_word,此前误写到 input(校验拦截);已关应用→迁移字段→重启。 +- 循环变量警告:校验器 definedVars 不认循环容器运行时注入的 item/index/value(引擎 ctx.vars 注入),已在 flowValidation.ts 加 LOOP_VARS 映射(计数循环支持自定义索引名),25 校验测试过,已提交。 +- 淘宝流程选择器/必填字段确认仍齐备(updatedAt 04:29 未再被覆盖)。 + +## 淘宝流程:搜索后加延迟+关弹窗+等结果 +- 用户要求:点搜索后加延迟、加一个点关闭按钮的节点。 +- 发现点搜索原本直连抓取节点(无任何等待)。已插入 3 节点:Basic_Delay_V2(3s) → logic_control_try_catch(尝试关闭登录弹窗,try_fn 内 Element_Click_V6 通用 close 选择器、is_verify=0、超时 6s,弹窗没出现则吞错继续) → Element_WaitElement(css:[id^="item_id_"], 40s,自适应等结果)。 +- 槽位机制备忘:容器子节点用 parentId + slot 字段(try_catch 的槽是 try_fn/except_fn,数组循环是 array_for_fn)。 +- 关应用→改→重启完成,21 节点。 + +## 运行时跟随前台标签页(关键修复) +- 现象:运行记录里 loop_click_close 报"未找到元素"(用户明明拾取过),四个抓取节点 2-3ms "成功"实为在旧标签页抓空,AI 报"输入文本为空"。 +- 根因:拾取器做了前台标签页跟随,但运行链路没有——点搜索开新标签页后,元素操作仍附着旧页。 +- 修复:session.evaluate 入口加 followForegroundTab()(/json/list 查前台页,变化则 activateTab,400ms 节流防高频轮询放大)。监听器只走 cdp 无递归风险。 +- 25 单测过,已提交,dev 重启生效。注意:需要后台页操作的场景应显式用「切换标签页」能力。 + +## 搜索按钮选择器修正(真实页面探测) +- 点搜索按钮失败:猜的 css:#J_TSearchForm > div:nth-of-type(2) > button 层级不对。 +- 连 61619 实例探首页:表单 #J_TSearchForm 还在,按钮是 BUTTON.btn-search(DIV.search-button 内),新选择器 css:#J_TSearchForm button.btn-search 命中 1 个、文本"搜索"。 +- 淘宝首页当前 front tab 已是 s.taobao.com/search(说明上次运行其实点进去了或手动操作过)。已关应用→改流程(uiSelector+cssSelector 同步)→重启。 + +## 点搜索"未找到元素"= 页面渲染时序问题 +- 现象:选择器已验证正确(btn-search 命中 1),运行时却 17s 找不到;事后连上同一标签页按钮存在、readyState complete。 +- 根因:淘宝首页 #q 输入框是静态 HTML 早出,.btn-search 按钮 JS 渲染慢;点击重试 3×5s 仍未出。 +- 修复:node_wait_search 从等 #q 改为等 #J_TSearchForm button.btn-search(等按钮=整块渲染好),timeout 30s。重启完成。 + +## 「点到了还报错」澄清 + 搜索按钮选择器再次回滚 +- 用户看到"点击了搜索按钮"是自己在 Edge 窗口里手动点的;流程节点本身因选择器不匹配从未点到,等 17s 后报 not_found。页面跳转≠流程节点成功。 +- node_click_search 的 uiSelector 被应用内重新拾取/编辑覆盖回结构化路径 #J_TSearchForm > div:nth-of-type(2) > button(label/cssSelector/position 混合状态证实),结构路径在冷启动新页面渲染顺序变化后匹配不上。 +- 修复:uiSelector 与 cssSelector 统一为稳健的 #J_TSearchForm button.btn-search(按 class、不依赖层级序号),关应用→改→重启完成。 +- 教训:拾取器生成的结构化路径对动态渲染页面不稳,同类站点节点优先用 class 特征选择器;改完流程后让用户避免对已修节点重拾。 + +## 修复:点击后页面跳转被误报为点击失败(13:15) +- 用户澄清:搜索按钮点击是流程节点点的(非手动),页面也跳了但节点报错 +- 根因:click() 点击后 180ms 回读 el.dataset.rpaClickToken 验证"可信点击",但按钮触发跳转 → 同页 context 销毁/新标签页被前台跟随切走 → 回读必然失败 → 误报"目标未收到可信点击"(还带满超时重试,失败得慢) +- 修复(session.ts):回读改为快速尝试(1 次 + 400ms 后 1 次),失败后用 navigationEvidence(beforeTargets, beforeUrl) 判定:新开标签页 / URL 变化 / evaluate 抛 context destroyed → 视为点击已生效,返回 { navigated: true } +- browser.ts:日志加"(页面已跳转,点击生效)" +- tsc 通过;dev 已热重启生效 + +## 修复:多标签环境前台跟随切错页 + dev 未重编译坑(13:21-13:28) +- 用户重跑仍失败:run_mw33knx1 run.json 显示 node_wait_search 成功(searchReady=true)但 0.5s 后 node_click_search "Element not found: #J_TSearchForm button.btn-search" +- 根因 1:followForegroundTab 在每次元素操作前无条件切到 pages[0],浏览器有多个旧标签页时切错页;node_open openNewTab=1 新开的页不是 pages[0] +- 修复(session.ts):evaluate 加 follow 参数(默认 true 兼容);withElementOnce / locate 改两段式——先在当前激活页找,找不到才 followForegroundTab 后重试一次 +- 根因 2:electron-vite dev 一直没重编译(out/main 还是 13:13 产物),13:15 的修复实际未生效;重启时还踩坑 ELECTRON_RUN_AS_NODE=1(WorkBuddy 继承)导致 electron.app undefined → env -u 清除后正常 +- 教训:验证代码生效要看 out/main 产物内容,不能只看进程 PID;重启 dev 必须用 env -u ELECTRON_RUN_AS_NODE +- 新 dev 任务 id: acdZmO + +## 修复:相似元素列表全重复 + AI 不认 hint_word(13:29-13:35) +- run_mw33t9g5:点搜索/关弹窗/等结果全过,抓到 46/46/46/29 条,但每列值全相同(46 个标题同一句直播广告、价格全 4799) +- 根因 A:动态 id 泛化使 46 张卡的引用选择器全为 [id^="item_id_"] > ... 相同串 → getSimilarElements 46 个引用全命中第一张卡 +- 修复:BrowserElementReference 加 index?; selectorSpec 透传; getSimilarElements/getChildren 生成引用时算 querySelectorAll().indexOf(node) 写入 index; withElementOnce 与 locate 的 css/xpath find 按 index 取(index>0 用 querySelectorAll[n]) +- 根因 B:ai-caps.ts inputText() 候选键不含 hint_word("输入文本为空")→ 加入 hint_word +- electron-vite dev 确认不自动重编译主进程,每次改完必须重启 dev(env -u ELECTRON_RUN_AS_NODE npm run dev);验证看 out/main/index.js 内容 +- 新 dev 任务 id: HtSyND + +## 修复:about:blank 残留页 + try_catch 捕获语义不明(13:41-13:47) +- 用户问:为啥浏览器有 about:blank 页;关闭按钮明明"已经关了"却一直报找不到 +- about:blank 根因:外部浏览器启动参数固定带 about:blank 初始页(external.ts startUrl 兜底),openNewTab 再开新 tab 后空白页永远残留(还是之前前台跟随切错页的元凶之一) +- 修复(session.ts openNewTab):新开 tab 前先找 about:blank/chrome://newtab/edge://newtab 空白页,有则 activateTab+navigate 复用,没有才 openNewPageTarget +- 关闭按钮:try_catch 兜底本来就该"找不到=没弹窗=继续",但引擎只把错误写进 ctx.vars.error 无日志说明,用户误以为故障 +- 修复(runtime.ts try_catch catch 块):加 warn 日志「子步骤未成功,已捕获并继续执行:<原因>」 +- dev 重启任务 id: L9eDv1 + +## 关闭按钮选择器漂移 + 关键教训:electron 内存代码≠磁盘产物(13:47-13:56) +- run_mw348pyc(13:39,AI 503 前一步)数据仍全重复 → 发现 electron-vite dev 只重编译不重启 electron:13:27 启动的进程内存里一直是旧代码,13:35 我 grep 磁盘产物验证是假象;13:47 重启后 index 修复才首次真正进内存 +- 教训:验证修复生效必须重启 electron 进程本身,grep 磁盘产物不够 +- 关闭按钮"刚才能点现在点不到":html > body > div:nth-of-type(4) > img 结构序号随 body 动态内容漂移 +- 修复:flow 文件 loop_click_close 改为 css:img[src*="close"](try_catch 兜底,找不到就跳过);拾取器 stable() 增强——IMG 元素优先用 img[src$="文件名"] 特征(无反斜杠,模板字面量安全) +- flow 运行时每次 getFlow 读磁盘,改文件无需重启;但用户在画布点保存会覆盖 +- AI 503 是服务端暂时不可用,重试即可 +- dev 任务 id: uKXMoA + +## 真凶落网:NodeList 没有 indexOf(14:00-14:11) +- 13:57 用户重跑数据仍全重复(这次确实是新代码)→ index 修复本身有 bug +- 写 e2e 测试 similar-list.test.ts(46 张卡模拟淘宝 item_id_ 动态 id + 无头 Chrome CDP)复现:getSimilarElements 返回引用 text 各不同但 index 全部缺失 +- 根因:`root.querySelectorAll(...).indexOf(node)` —— NodeList 没有 indexOf 方法,TypeError 被 catch {} 静默吞掉,index 从未写入 +- 修复:[...querySelectorAll(...)] 展开后再 indexOf(getSimilarElements + getChildren 两处) +- e2e 4/4 通过:46 引用 index 互不相同、标题/价格/parent 三列 unique=46 +- 踩坑记录:测试页 DOM 结构要贴合淘宝分层(标题独占 div:nth-of-type(3)),否则选择器 0 命中或 184 条 +- 流程已重启(L4lL0X),等用户重跑验证 Excel 写入 + +## 流程全程跑通 + 补 Excel 落盘(14:13-14:20) +- run_mw35g6d8 succeeded!47 条标题 46 唯一、价格 34 唯一、AI 分析 1844 字完整输出——数据重复问题正式解决 +- 用户"什么都没得到":流程末尾只有内存表(Bivartable),没有 Excel 落盘节点 +- 修复 1:末尾加 node_save_excel(Bivartable_DfWriteToFile,桌面/淘宝手机比价.xlsx),edge: node_write_ai → node_save_excel;流程运行时读盘,无需重启 +- 修复 2:node_init_rows 表头 bug——value 是一维数组被 Bivartable 当 4 行,改为 [["标题","价格","店铺","销量"]] +- 立即交付:scripts/export-last-run.js 从 run.json 的 tableRows(python str 风格序列化,ast.literal_eval 解析)重建表格,exceljs 写桌面《淘宝手机比价.xlsx》(比价结果 47 行 + AI分析 sheet) + +## 修复:Excel 落盘 EBUSY(14:31-14:36) +- 用户跑流程保存 Excel 报 EBUSY: resource busy —— 目标文件正被 WPS/Tencent 预览打开,Windows 锁文件 +- 修复(data-table-common.ts Bivartable_DfWriteToFile/DoSave):捕获 EBUSY/EPERM/占用类错误,自动另存为 `原名_20261105_143500.xlsx` 时间戳文件并记 warn 日志,流程不失败 +- 用户侧解决办法:跑流程前关掉正在预览的 Excel +- dev 重启任务 id: tn3Ik9 + +## 生成 Word 版比价报告(14:39-14:55) +- 用户嫌 Excel 表格不好看(列窄要手动拉开),要求 Word 版 +- 路由:workbuddy-office-routing → 带材料生成 Word → workbuddy-local-office-edit(editor_sdk MCP,edsdk.py 封装) +- 流程:create_doc → doc_modify_section landscape(横向,表格宽)→ doc_insert_markdown(file:// 方式插大段 md)→ doc_set_table_properties(列宽 manual dxa、表头 D6E4F0 底色、隔行 F5F7FA)→ save_file 到桌面《淘宝手机比价报告.docx》→ present_files +- 踩坑 1:md 表格内 `|` 用 `\|` 转义不被引擎识别,会把行拆乱 → 用全角| +- 踩坑 2:抓取数据里混有 \r 和 \n(价格 "17739\n.04"),未清洗导致幻影行(50 行 vs 预期 48)→ 生成前所有列统一 replace \r\n + strip +- 踩坑 3:doc_modify_section 必须传 section_index(schema 未标必填但服务端要求) +- 跑流程自动出 Word 版:可在 flow 末尾加节点?暂未做——目前 Word 报告是手工生成的;如需每次运行自动生成 Word,后续可加能力节点 + +## 修复:关弹窗点击生效却等待+报错(14:51-14:58) +- run_mw367eym 整体成功但 loop_click_close failed:用户观察到"弹窗关掉了但等很久还报错" +- 根因链:点击关闭按钮 → 弹窗整体从 DOM 移除 → token 回读找不到元素 → 判失败 → 引擎 6s 超时内反复重试 → 弹窗已没了,每次 locate 报"未找到元素" +- 修复(session.ts click):readToken 捕获 not_found 时置 elementGone;回读失败 → 先 navigationEvidence(跳转)→ 再 elementGone(元素消失=弹窗被关)→ 返回 { dismissed: true } 成功 +- browser.ts:日志区分「(页面已跳转,点击生效)」「(弹窗已关闭,点击生效)」 +- 注意:用户重新拾取过关闭按钮,uiSelector 已变 img[src$="TB1QZN...png"](cssSelector 仍是 img[src*="close"],运行用 uiSelector) +- dev 任务 id: eboY4I + +## 新流程:晨间助手全家桶(14:58-15:40) +- 用户想测不同功能,盘点 catalog 1016 组件中**已注册实现**的能力后,设计 30 节点长流程 flow_morning_assistant.json: + A 文件整理(scan 下载测试沙盒 → mkdir 4 类目录 → 遍历+IF 按扩展名移动)→ B 记事本键鼠(App_Run notepad → Keyboard_Send 摘要 → 截图 → App_Kill)→ C 数据加工(DoOpenExcel 读淘宝比价表 → Sort/Filter/Min/Max/Avg → DfWriteToFile 新表)→ D OCR+AI(截图 OCR → AI 总结 → 剪贴板 → 弹窗通知) +- 沙盒:桌面/RPA整理测试/ 7 个样例文件 +- 生成脚本 scripts/gen-morning-flow.py;校验:组件存在+必填字段 0 错误;表达式(contains/lower/len/round/||)用真实引擎 vitest 4/4 通过 +- 坑:测试文件相对路径深度(__exprcheck 在 4 层,需 ../../../../packages);@rpa/core 不导出 expression 模块,需从源码相对路径导入 +- 待用户在应用里跑一遍验证(运行记录节点耗时/IF 分支走向/OCR 质量/AI) + +## 修复:File_File_Move 目标为目录时 ENOTDIR(15:41-15:44) +- 晨间助手首跑失败:遍历移动文件报 "cp returned ENOTDIR (cannot overwrite directory ...\其他 with non-directory ...背景音乐.mp3)" +- 根因:files.ts 的 File_File_Move 把 pathDest/dest 直接当完整目标文件路径(rename 文件→目录失败,fallback cp 也报 ENOTDIR);我的流程里 pathDest 填的是分类目录 +- 修复(files.ts):目标存在且 isDirectory 时自动 path.join(target, basename(source))("移入目录"语义),目标不存在仍按完整路径 +- tsc 通过;编译产物 15:43:51 确认含新逻辑;dev 任务 id: 7DlMEh +- 待验证:重跑晨间助手(IF 分支走向、OCR、AI、记事本环节) + +## 打包(15:42) +- `RPA_SKIP_TESTS=1 npm run package` 跳过单测打包成功(约 9 分钟)。 +- 注意:脚本清理 release/dist 旧产物时 fs.rmSync 会被环境的批量删除保护拦截(>50 文件需确认),需先手动删掉 release/dist 下的旧产物再跑。 +- 产物:release/dist/RPA-Studio-Setup-0.1.0-win-x64.exe(sha256 b213c91f…)、RPA-Studio-0.1.0-win-x64.zip(92ebbf3f…)。 + +## 晨间助手全家桶首跑成功(15:55) +- run_mw393tg6 succeeded,26 节点全成功 +- 实证:7 个样例文件 100% 正确归类(zip→压缩包、png→图片、docx/pdf/txt→文档、mp3/exe→其他);桌面《低价手机清单.xlsx》生成(min 98 / max 15999 / avg 6360.75);OCR 识别出屏幕文字;AI 总结 100 字完整;截图存根目录 +- 小瑕疵:截图时机在记事本关闭后(OCR 抓到的是桌面而非记事本内容);AI 从 OCR 文本联想出"遇小插曲"属幻觉 +- 测试通过:文件操作+IF+遍历、App 控制+键鼠、Excel 读+排序筛选统计、OCR+AI+剪贴板+弹窗 全链路验证 + +## 新流程:Word 报告加工(16:05-16:12) +- 用户选了 F 方案。flow_word_report_process.json(11 节点):Word_Open 比价报告 → ReadAll 全文 → Replace「RPA 自动采集」→「(已加工校验)」→ Enter 空行 → Write 追加加工说明(含 ${len(docText)} 字数)→ InsertPicture 整理完成截图 → Save → File_Copy 复制「_加工版.docx」副本 → Close → 弹窗 +- Word 能力是 PowerShell COM 实现(每节点独立开关文档,隐藏窗口);Word_Open 仅返回 {path} 句柄,objWord 传对象 handlePath 兼容 +- 坑:Word_SaveAs 与 Save 实现相同(只返回路径,不真正另存)→ 用 File_Copy_V1 造副本(target_path=目录 + rename=1 + name=新名) +- 校验 0 错误;len() 支持字符串已 vitest 验证;start 组件 id 是 Trigger.Manual(非 Basic_DefaultStart),勿漏 seq.append(start) +- 生成脚本 scripts/gen-word-flow.py;待用户运行验证(提醒:别开着报告预览,文件锁会干扰 Word COM) + +## 修复:算术验证码识别"不明白"(16:15-16:22) +- 用户反馈算术验证码识别有问题。排查:OCR_CalculationCode/CommonEnglishNumbersCode 等 32 个 OCR 组件全部共用 remaining-core 通用实现(@gutenye/ocr-node PaddleOCR 整图 detect 返回原文) +- 实测(PIL 生成 5 张算术验证码图):干净图上 OCR 识别全对("3+5=?")——根因不是识别不出来,而是**返回算式原文没有计算结果**,流程无法直接填答案;真实验证码有干扰时符号还会被认成 x/X/*、全角 +- 修复(新文件 captcha-caps.ts,last-wins 覆盖):OCR 原文 → 归一化(全角/×/长破折号/杂字符)→ parseCalculation 解析算式 → 计算并返回结果字符串;解析失败返回清洗原文+warn 日志;CommonEnglishNumbersCode 加字符清洗 +- index.ts 加载顺序 remaining-core → captcha-caps;测试 captcha-caps.test.ts 12/12 通过(解析层 7 单测 + 5 张真实 OCR 端到端 8/5/24/43/81) +- 坑:vitest ESM 无 require;测试文件 import 顺序决定 last-wins 覆盖(remaining-core 必须在 captcha-caps 之前);ctx.log 级别是 warn 不是 warning +- 测试图生成:PIL+干扰线 D:/office-test2/ocr-test/captcha_*.png;应用已重启验证产物含新实现 + +## 解答:锁屏组件为啥要填用户名密码分辨率(16:46-16:50) +- 用户问 ScreenControl_Screenlock 为啥要填 username/pwd/width/height/scale_factor/port +- 真相:实现只是一行锁屏命令(LockWorkStation),不读任何参数;这些必填字段是 catalog.json 照抄原厂 RPA「远程会话锁屏」定义带进来的(原厂锁屏需账号密码重建远程会话+设定分辨率缩放比),本机锁屏用不上,但校验器强制填 → 纯干扰 +- 修复:catalog.json 里 Screenlock fields 清空 + 补 label「锁屏」;ScreenUnlock 补 label「解锁屏幕(仅唤醒,登录锁屏需手动输入密码)」 +- 顺手:captcha-caps.ts 动态 import 改静态(消除 vite 打包警告),回归 12/12 通过 +- catalog 启动时加载,已重启应用生效 + +## 全量参数审计:假必填清零(16:52-17:00) +- 用户追问"还有没有没用但要填的参数 / 有用但没有的参数" → 写 scripts/audit-catalog-params.py 全量对比 catalog 字段 vs 实现读取 +- 审计方法:正则提取每个 register handler 函数体,收集 text(args,["k"])/args.k/pick 等直接读取 keys;args 整体传走标 indirect(keys 未知,不判);必填 key 再用全文兜底(带引号 + .key 词边界两种形式) +- 结论 A:**96 个假必填字段(72 组件)已全部改为选填**——最大族 web×31(WebBrowser_* 实现固定用当前会话)、designated_obj×23(元素句柄,校验器本来就有豁免)、accuracy2×3(图像查找只读 accuracy)、emails×3、其余散布(StrUtil 系列/Word_SaveAs fileAddr/Trigger.entryName 等) +- 结论 B:**"有用但目录没有的参数"为 0**——实现读取的 key 都被目录覆盖(保守审计),已知例外是实现缺失(如 Word_SaveAs 没真正另存)而非缺字段 +- 坑:审计脚本初版误报 313 个——间接调用(selectorOf(args))读的 keys 提取不到;args.min_second 点号访问无引号,兜底正则必须两种形式都查 +- 校验器 requiredInContext 已有条件豁免(designated_obj 仅 designated_info 开启时必填),目录改选填后 UI 红星同步消失 +- 审计脚本留在 scripts/,后续新增组件可复跑防回归