--- name: fund-e-holdings description: 通过 ADB 读取已连接 Android 手机中基金 e 账户的持仓,整理基金代码、份额、净值、资产及数据日期,并核对总额、比较前后变化。适用于用户要求查看或导出该应用持仓,以及排查读取所需的 USB 调试权限、自动唤醒并解除无密码锁屏后继续任务、在误触后恢复应用首页。 --- # 基金 e 账户持仓读取 一次运行完成连接检查、唤醒解锁、打开应用、等待启动、回到首页、进入持仓、核对金额、保存结果、退到后台、自动息屏,再返回结果。读取使用用户已登录的应用界面。每次查询先回到并确认首页,再进入持仓页;即使当前已经在持仓页,也不直接读取旧页面。优先提取 UI 文字并在本地核对,正常流程无需截图。首次读取交付明细、币种、数据日期和总额;更新检查默认只报告变化。仅在用户需要文件时生成 CSV 等交付物。 ## 范围 - 包名为 `com.chinaclear.fund`。本流程只浏览、打开应用、处理已识别的只读查询提示、点击查询入口和滚动列表;登录、验证码、人脸或指纹验证由用户在手机完成。 - 读取请求不包含交易、添加账户、修改账户设置或发送导出文件的授权。应用「导出」可能进入分享流程,先检查实际目标,不能默认发送。 - 不提取登录凭据、应用私有数据库或绕过系统权限。截图与 XML 是临时工作资料,保存在当前任务的 `work/`;不把个人持仓、设备序列号、姓名或账户信息写进 Skill。 ## 执行环境 先确认当前任务的命令执行环境能访问连接手机的电脑、Python 和 ADB。仅在网页或云端安装本插件不会提供本机 USB 连接;若没有可访问手机的本地执行能力,明确说明缺少的环境并请用户转到本地任务,不在云端反复安装 ADB 或把未发现设备解释为手机拒绝。 ## 连接和权限 1. 用 `adb devices -l` 或附带工具 `probe` 检查实时连接。macOS 缺少 ADB 时按用户偏好使用 `brew install --cask android-platform-tools`;只读取界面不需要安装 scrcpy。 2. 多台设备连接时明确选择目标;`unauthorized` 需要用户在手机允许 USB 调试,`offline` 需先恢复连接。沙箱限制访问 USB、ADB 服务或下载时使用正常提权流程,不能把电脑环境错误误报为手机拒绝。 3. 先由结构化读取命令检查页面特征;无法识别时再看异常截图。用户说已打开应用也可能实际停在桌面或设置页,不沿用旧坐标。 4. `input tap` 可能输出 Java 异常但退出码为 0。必须检查 `SecurityException` / `INJECT_EVENTS`,不能仅凭返回码宣称点击成功。 5. 小米/HyperOS 出现 `INJECT_EVENTS` 错误时,请用户检查「设置 → 更多设置 → 开发者选项 → USB 调试(安全设置)」。这是普通「USB 调试」之外的开关;先说明它允许已授权电脑模拟点击、滑动和输入,作用于整个手机,再由用户手动开启。开启、可能要求的账号/SIM 条件以手机提示为准;不要主动开启 USB 安装、停用授权超时等无关选项。 6. [scrcpy 官方说明](https://github.com/Genymobile/scrcpy#prerequisites)要求启用该开关后重启。可以先测试,若点击已经生效就不额外重启;否则让用户按说明处理。若用户只希望读页面,也可让用户手动导航后继续截图,无需扩大权限。 ## 息屏后唤醒与继续 `holdings.py` 是默认的全流程入口,不要求用户预先打开应用或停在持仓页。查询及恢复首页前自动检查屏幕及系统锁屏状态。息屏时发送 `KEYCODE_WAKEUP`(不使用会切换状态的电源键),确认屏幕已唤醒后,通过系统 `wm dismiss-keyguard` 请求解除锁屏;只有核实 `showing=false`、`inputRestricted=false` 且屏幕为 `Awake` 才继续。已亮屏解锁时不发送额外输入。 这适用于用户已授权读取、手机已连接且 ADB 已授权的场景。无密码锁屏可由系统解除;若仍要求密码、指纹、人脸或其他验证,等待次数达到上限或状态格式无法识别,停止并请用户处理,不盲目滑动、输入凭据或修改锁屏设置。系统解锁成功不代表应用已登录;后续仍检查应用和页面,应用登录验证由用户完成。查询解锁后若前台不是基金 e 账户,会动态解析并打开该应用,核实前台窗口后等待启动画面结束;不重置应用或清除数据。随后复用首页恢复流程,再执行查询。只执行 `--recover-home` 时仍要求应用已在前台。 只想唤醒并检查解锁状态时可运行 `python3 scripts/android_ui.py wake`。该命令只处理屏幕和系统锁屏,不导航应用;`holdings.py` 已内置这一步,无需重复调用。底层 `capture`、`tap` 等诊断命令保持显式操作方式。 ## 读取、更新与核对 优先使用 `scripts/holdings.py`,不拆成每次点击后都截图的多轮操作。以下路径相对本 Skill 目录,调用时展开为绝对路径;输出放在当前任务私有的 `work/`,不要放入插件缓存或仓库。 ```bash # 一条命令完成:息屏/桌面 → 唤醒解锁 → 打开应用 → 首页 → 持仓 → 保存结果 → 退到后台 → 息屏 → 返回结果 python3 scripts/holdings.py --out /absolute/task/work/fund-e ``` - 默认命令每次都执行「首页 → 持仓」并验证页面切换;已在首页时直接确认首页后进入持仓,已在持仓时先返回首页。`--reopen` 保留为兼容旧命令的选项,效果与默认查询相同,不提供跳过首页的查询参数。 - 多设备用 `--serial SERIAL` 明确选择。自动打开应用后先恢复到首页;原生首页入口或有限次返回用于已有页面。启动或加载画面没有可读内容时最多检查五次,不按返回退出应用。每次点击最多读取三次页面状态,失败就停止,不重复点击。 - 默认查询自动处理已识别的「温馨提示」:同时匹配查询范围、查询时效、免责声明和“我已阅读并理解以上提示信息”,唯一定位复选框及“确定”;未勾选时先勾选并核实,再确认一次。重新读取后必须确认覆盖页消失,才能继续首页导航或读取持仓。启动时的固定提示“系统检测到您的运行环境异常,请您注意账户安全。(敏感配置)”只有在消息、标题及唯一“确定”控件都匹配时才确认一次,并将原提示写入返回的 `warnings`;不修改设备配置。其他登录、输入、交易或确认控件继续返回 `needs_review`,不得套用这些例外。 - 返回的 `workflow` 记录唤醒、启动、首页、查询、退到后台及息屏结果,`stage` 为 `complete` 才表示全流程成功;失败停留在 `wake`、`launch`、`home`、`query`、`background` 或 `sleep` 并附带原因,不能只报告局部成功。任务运行结果保存到私有 `workflow-*/result.json`。 - 查询成功且快照、比较基线已保存后,先发送 `KEYCODE_HOME` 返回系统桌面,核实基金 e 账户已不在前台;随后发送 `KEYCODE_SLEEP` 并核实系统进入 `Asleep` 或 `Dozing`;不使用会切换状态的电源键。`--visual-check` 的持仓截图在息屏前保存。查询失败或仅恢复首页时保留现场,不自动息屏。若查询已成功但退到后台或息屏失败,保留已验证的持仓及基线,返回 `needs_review`、`stage: background` 或 `stage: sleep` 并明确说明失败步骤。 - 首次成功读取创建 `snapshot.json` 和 `latest.json`,返回完整持仓。后续复用同一 `--out`,默认与 `latest.json` 比较,只返回日期、总额、变化字段及新增/移除的基金;完整快照仍保存在本地。可用 `--baseline /absolute/previous/snapshot.json` 指定比较来源;成功后仍更新输出目录中的 `latest.json`。 - 基线绑定设备,金额用 `Decimal` 精确核对,基金代码保留前导零。相同代码的完全相同卡片去重;记录冲突、缺字段、总额不符、日期倒退或不同布局均返回 `needs_review`(退出码 2),保留原基线并尝试生成一张异常截图。不要把失败当成无变化,也不要用份额乘参考净值代替应用资产金额。 - 快照范围是**当前产品列表**,不是自动证明全部账户。账户分组不自动合并;当前解析器仅支持人民币的已知产品卡片布局。应用不在 XML 中可靠暴露登录身份、筛选状态,因此换账户或筛选范围时使用独立输出目录,不拿旧范围作变化比较。 - XML 的离屏或零尺寸节点可以提供文字;在卡片结构完整且明细合计与页面总额一致时保留,不为每个节点滚动截图。它们的坐标不能用来点击。首次核验、用户要求目视复核或结构变化时加 `--visual-check`,或按需要滚动截图检查。若 XML 与截图冲突,以实际观察说明缺口,不能宣称数据完整。 - XML 可能同时保留持仓内容和覆盖其上的「温馨提示」确认页。必须先检查前台窗口以及登录、输入、确认控件,再判断页面和解析持仓;上述已知提示按限定流程处理;其余覆盖页返回 `needs_review`,不写入基线。不能仅凭「总资产」等文字或金额合计宣称已进入持仓。用户指出画面不一致时,立即查看新截图并撤回未经验证的成功结论。陌生提示页需查看截图和用户指令后再处理。首次全流程验证和用户指出异常后,使用 `--visual-check` 并查看最终截图,确认实际已到持仓页。 - `status: ok` 表示已通过已知阻塞控件检查、结构化读取及金额核对,不是对任意未知覆盖层的视觉证明。页面切换只证明导航成功;`source_freshness` 明确标记服务端刷新未验证。保留应用数据日期,不把抓取时间当成数据日期,也不把“无变化”解释为已联网获取最新行情。 ## 误触后恢复首页 用户要求回到首页,或已授权的查询因误触进入其他页面时,使用恢复命令,不重复点击旧坐标: ```bash python3 scripts/holdings.py --out /absolute/task/work/fund-e --recover-home ``` 命令检查前台应用,优先点击当前 XML 中原生底部导航的「首页」按钮;没有可用入口时发送 Android 返回,最多三次。每次操作后检查页面变化,已在首页则不操作;只有再次识别到首页的「公募基金查询」和「点击查询」特征且没有阻塞控件,才返回 `home_verified: true`。正常恢复不截图,异常时保留一张截图及原因。 恢复只改变页面位置,不重新查询或修改 `latest.json`,也不撤销已经发生的账户或交易变更。仅要求恢复时停在首页;若原任务还包含持仓更新,恢复成功后再运行默认查询命令。`--recover-home` 与 `--reopen`、`--baseline` 不同时使用。 遇到登录、输入、交易或确认控件、前台离开应用、页面无变化,或达到返回上限时停止并报告 `needs_review`。单独的恢复首页命令不自动确认弹窗、保存表单、重置应用、清除数据或反复返回。页面无法自动识别时查看异常截图再判断,必要时由用户处理当前页面。 基金详情页的滚动标题可能让 `uiautomator` 报 `could not get idle state`。此时不要连续重试 XML,也不要立即盲目返回。先查看命令保存的新截图;若清楚确认是有返回入口的只读详情/查询页面且没有登录、交易、确认或编辑内容,重新检查前台仍为该应用后,用 `adb -s SERIAL shell input keyevent KEYCODE_BACK` 返回一次,再运行恢复命令验证。仅看到返回命令执行成功不算恢复成功;新截图不清楚或仍失败时停止并说明。 ## 按需诊断 `scripts/android_ui.py` 提供底层 ADB 操作,只依赖 Python 标准库和本机 ADB: ```bash python3 scripts/android_ui.py probe # 默认只保存 XML 和文字;XML 不可用时才自动截图 python3 scripts/android_ui.py capture --out /absolute/task/work/fund-e # 需要观察页面时显式截图 python3 scripts/android_ui.py capture --out /absolute/task/work/fund-e --screenshot always python3 scripts/android_ui.py launch python3 scripts/android_ui.py tap X Y python3 scripts/android_ui.py swipe X1 Y1 X2 Y2 --duration 500 ``` `--serial` 放在子命令前。`capture --screenshot never` 完全关闭截图;返回警告时需检查原因,不能把缺少文字视为成功。`launch` 动态解析启动入口,不依赖固定 Activity。 截图坐标使用原始像素,不能直接用展示时缩放后的坐标。已观察到 WebView 滚动后 XML 坐标仍停留在旧位置;手动操作后重新读页面,必要时从新截图定位。不要以 XML 坐标没变判断滚动失败。截图黑屏或禁止截屏时不要绕过保护。截图与 XML 不是原子快照,页面变化时应重新核验。 ## 设备特定问题 遇到小米权限或拔出 SIM 卡后的行为问题时,读取 [设备验证记录](references/device-validation.md)。它记录一次实际测试的范围,不代表其他设备或后续会话的保证。