OpenCLI Mercury 适配器实战:用浏览器桥接驱动 Mercury 报销草稿的安全写入
开发工具CLI人工智能AI 应用浏览器控制GUI 自动化【免费下载链接】OpenCLIMake Any Website into CLI Use your logged-in browser by AI agent.项目地址https://gitcode.com/gh_mirrors/ope/OpenCLI点击查看免费下载导读本文围绕 OpenCLI 仓库中 Mercury 适配器的完整实现docs/adapters/browser/mercury.md讲解如何在没有公开 API 的前提下通过 OpenCLI Browser Bridge 驱动app.mercury.com的可见报销页面完成上传票据 → 等待 OCR → 纠正被覆盖字段 → 停在 Review 审核页的安全写入链路。读完本文你将掌握reimbursement-plan、check-login、reimbursement-draft三个命令的参数语义、输入校验规则、底层调用链与测试方法并理解适配器刻意不点击最终Submit expense按钮的设计原因。Mercury 报销在 OpenCLI 中属于浏览器 UI 流程 Browser 模式。它没有公开 API也没有稳定的 JSON 接口用于创建报销单因此适配器直接驱动 Mercury 报销页面的可见元素创建草稿、上传本地票据文件、等待 Mercury OCR、再重新应用 Agent 提供的字段——因为 OCR 可能会覆盖金额、币种、日期或商户名。写入命令被刻意设计为保守它止步于 Mercury 的 Review 步骤绝不点击最终的Submit expense按钮任何最终提交都必须由人类或负责任的 Agent 在检查 Review 页面后完成。Mercury 适配器概览与设计动机为什么需要浏览器 UI 驱动从仓库的适配器清单cli-manifest.json可以看出Mercury 的三个命令被声明为Strategy.UI策略reimbursement-plan为Strategy.LOCAL其中reimbursement-draft声明了access: write、browser: true、siteSession: persistent、defaultWindowMode: foreground。这些声明直接对应本文核心结论无公开 API 可调用创建报销只能走页面 UI必须复用已登录的浏览器会话适配器不做 OAuth 登录流而是使用 OpenCLI 选中的浏览器 profile 里已有的登录态siteSession: persistent写入是前台可见的foreground窗口模式让人类可以随时看到适配器在页面上做了什么这与Review 不提交的安全设计互相配合。适配器实现的三个命令原文档给出的命令总表如下这是适配器对外暴露的全部能力CommandDescriptionopencli mercury reimbursement-planValidate a reimbursement payload locally without opening Mercuryopencli mercury check-loginOpen Mercury reimbursements and report whether the selected browser profile is logged inopencli mercury reimbursement-draftCreate a reimbursement draft, attach the receipt, correct OCR-overwritten fields, and stop at Review三个命令在源码中的注册位置分别为 clis/mercury/reimbursement-plan.js、clis/mercury/check-login.js 和 clis/mercury/reimbursement-draft.js共享同一套输入归一化逻辑 clis/mercury/utils.js。参数详解reimbursement-plan与reimbursement-draft的共享负载reimbursement-plan和reimbursement-draft接收完全相同的业务负载参数reimbursement-plan是纯本地校验不打开浏览器reimbursement-draft才真正执行页面写入。原文档参数表如下FlagMeaning--receiptAbsolute or relative path to a local receipt/proof file--amountOriginal-currency positive amount, e.g.140.00--currencyOriginal currency code, defaultCNY--dateExpense date asYYYY-MM-DD--merchantMerchant name to show in Mercury--categoryMercury expense category, defaultMarketing Advertising--notesBusiness purpose / reimbursement notes--ocr-wait-secondsSeconds to wait after receipt upload before reapplying fields, default8--close-after-reviewClose Review after verification; still never submits结合源码 clis/mercury/reimbursement-draft.js 中实际的参数声明可以补充以下精确语义--receipt、--amount、--date、--merchant、--notes均为必填参数--currency默认CNY--category默认Marketing Advertising--ocr-wait-seconds默认8--close-after-review是布尔开关默认false置为true时会在 Review 验证完成后点击页面上匹配close文本的按钮关闭审核对话框但仍然绝不触发最终提交源码在 clis/mercury/reimbursement-draft.js 中实现点击目标限定为文本归一化后恰好等于close的元素。源码级的输入校验规则所有参数都会先经过 clis/mercury/utils.js 的normalizeReimbursementInput归一化。这一步是本地先行策略的基石让非法输入在浏览器被打开之前就以ArgumentError失败。逐项规则如下票据文件parseReceiptPathutils.js相对路径会被path.resolve解析为绝对路径随后fs.statSync校验文件必须真实存在且是普通文件stat.isFile()否则抛出ArgumentError金额parseAmountutils.js先移除千分位逗号然后必须匹配^\d(\.\d{1,2})?$且数值大于 0即正数、最多两位小数如140.000、1.999都会被拒绝币种parseCurrencyutils.js统一转为大写必须匹配^[A-Z]{3}$的三字母 ISO 货币代码US、cny会自动大写为CNY后通过等不合规输入会在本地被拦截日期parseDateutils.js必须匹配YYYY-MM-DD且经过Date.UTC往返校验是真实存在的日历日期——2026-02-30这类不存在的日期会被拒绝OCR 等待秒数parseOcrWaitSecondsutils.js必须是非负整数字符串abc等非法输入直接失败布尔开关optionalBooleanutils.js接受1/true/yes/y/on与0/false/no/n/off字符串其他值抛错。这些校验规则被测试用例 clis/mercury/mercury.test.js 逐一覆盖amount: 0、amount: 1.999、currency: US、date: 2026-02-30、ocr-wait-seconds: abc、不存在的票据路径都会抛出ArgumentError。Agent 工作流三步完成一次安全的报销写入原文档给出了标准的 Agent 调用顺序。在真实落地时建议严格按 1→2→3 的顺序执行先确认登录态、再本地校验、最后才触碰页面# 1. Confirm the selected browser profile is logged into Mercury opencli --profile profile mercury check-login -f json # 2. Validate the payload locally first opencli mercury reimbursement-plan \ --receipt /absolute/path/to/receipt.png \ --amount 140.00 \ --currency CNY \ --date 2026-06-26 \ --merchant Example Merchant \ --category Marketing Advertising \ --notes Example business purpose. \ -f json # 3. Create the draft and stop at Review opencli --profile profile mercury reimbursement-draft \ --receipt /absolute/path/to/receipt.png \ --amount 140.00 \ --currency CNY \ --date 2026-06-26 \ --merchant Example Merchant \ --category Marketing Advertising \ --notes Example business purpose. \ -f json值得注意的细节check-login与reimbursement-draft是浏览器命令需要--profile profile指定浏览器 profilereimbursement-plan是纯本地命令不需要浏览器源码中声明为browser: false三个命令都建议追加-f json以 JSON 输出方便 Agent 程序化解析结果行传入的--receipt必须是本机真实存在的文件绝对路径reimbursement-plan阶段会做本地文件校验。执行链路reimbursement-draft从登录检查到 Review 的七步调用原文档描述了结果源码则给出了精确的执行顺序。结合 clis/mercury/reimbursement-draft.js 与 clis/mercury/utils.js完整链路如下登录态检查inspectMercury先goto到https://app.mercury.com/expenses/my-expenses常量定义于 utils.js再通过页面文本与 URL 判断loggedIn未登录时assertLoggedIn抛出AuthRequiredErrorutils.js创建表面防护assertCreateExpenseSurface探测当前页面是否已存在Review Submit expense 表单字段的组合。若发现已有报销审核打开命令直接抛出CommandExecutionError拒绝点击任何可能是最终提交的按钮reimbursement-draft.js打开新建报销clickCreateExpenseButton在可见的button/a/[rolebutton]/[rolelink]中查找文本为submit expense或new expense的元素若候选元素位于对话框/表单内或上下文包含 Review、receipt、amount 等关键词则判定为危险目标并拒绝点击utils.js上传票据并验证要求 Browser Bridge 提供uploadFiles能力reimbursement-draft.js以[data-testidexpense-attachment-upload]为目标上传文件并核验四点uploaded true、文件数恰好为 1、目标选择器精确匹配、文件名包含票据的 basename。任一点不符即抛错绝不带着未确认的上传继续等待 OCR 后纠正字段按--ocr-wait-seconds等待默认 8 秒然后fillReimbursementFields使用原生 setter 派发input/change/blur事件重新填写 amount、currency、date、merchant、notescategory 字段额外派发Enter键事件以触发自定义下拉的确认utils.js。填写后校验六个字段的touched标记全部为真缺失即抛错点击 Review 并快照在可见可交互元素中精确匹配文本review并点击reimbursement-draft.js随后reviewSnapshot断言页面出现Review文本且Submit expense按钮可见utils.js可选的关闭动作若传入--close-after-review才点击文本为close的元素关闭对话框然后返回结果行。这里体现出适配器的核心工程思想每一步都带后置条件postcondition验证任何一环失败都以类型化错误ArgumentError/AuthRequiredError/CommandExecutionError终止而不是返回部分成功的假象。Browser Bridge 的uploadFiles底层实现适配器对票据上传的要求依赖 Browser Bridge 的uploadFiles能力。该方法在浏览器内核中实现于 src/browser/base-page.ts其工作方式与适配器的严格校验正好呼应解析目标选择器此处为[data-testidexpense-attachment-upload]先在页面里确认目标是input[typefile]并检查multiple属性与accept约束打上一次性标记属性后优先通过setFileInput后端能力、否则通过 CDP 的DOM.setFileInputFiles注入本地文件base-page.ts上传完成后回读el.files中的文件名做验证这与reimbursement-draft中上传必须确认恰一个文件且名字匹配的断言相互印证。因此如果使用的浏览器后端既不支持setFileInput也不支持 CDP 文件注入reimbursement-draft会在上传前直接抛出CommandExecutionError提示需要 Browser BridgeuploadFiles支持见 reimbursement-draft.js。预期结果三个命令的返回语义原文档按命令分别定义了成功/失败语义结合源码列字段可以给出更完整的说明。check-login的结果status: ready表示 profile 到达了 Mercury 报销页且处于登录态status: needs_login表示 Mercury 重定向到了登录页需要在同一个 Chrome/OpenCLI profile 中登录后再重跑。源码输出列还包括loggedIn、url、hasSubmitExpense、hasReimbursements、titlecheck-login.js可供 Agent 进一步判断页面状态。reimbursement-plan的结果status: ready表示本地票据存在、金额/日期格式合法票据文件缺失、非正数金额串、畸形币种代码、非法日历日期、非法的等待秒数或布尔参数都会在打开 Mercury 之前失败抛出ArgumentError输出使用票据的 basename如receipt.png而不是绝对路径避免向外部暴露本机目录结构。这一行为由receiptBasename实现utils.js并被测试用例断言为确定性输出mercury.test.js。reimbursement-draft的结果成功路径返回一行包含以下字段的摘要uploaded: truereviewReady: truesubmitBlocked: truewarnings包含final Submit expense was intentionally not clickedMercury 页面停留在 Review 步骤且展示预期的 receipt、amount、currency、date、merchant、category、notes若上传确认、必填字段纠正或 Review 后置条件失败命令抛出类型化错误而非返回部分成功行此时应保持浏览器打开人工检查 Mercury 中的校验错误。从测试用例看成功路径还精确断言了副作用次数uploadFiles必须以[data-testidexpense-attachment-upload]为目标、只调用一次page.evaluate恰好调用 6 次mercury.test.js说明适配器对页面操作的每一步都有严格的确定性约束。测试与验证从仓库级检查到真实 UI 冒烟原文档给出三层测试策略均可在仓库根目录直接运行。仓库级检查npm run dev -- validate mercury npm run typecheck npm run docs:buildvalidate mercury会校验该适配器的清单与实现是否一致命令注册信息见 cli-manifest.jsontypecheck与docs:build保证类型与文档体系完好。本地输入冒烟不打开浏览器npm run dev -- mercury reimbursement-plan \ --receipt /tmp/example-receipt.png \ --amount 1.00 \ --currency USD \ --date 2026-06-30 \ --merchant OpenCLI Test Merchant \ --category Office Supplies Equipment \ --notes OpenCLI adapter smoke test; do not submit. \ -f json这一步只做纯本地校验不会打开 Mercury适合 CI 或无头环境。真实 UI 冒烟仅在测试环境执行npm run dev -- --profile profile mercury reimbursement-draft \ --receipt /absolute/path/to/test-receipt.png \ --amount 1.00 \ --currency USD \ --date 2026-06-30 \ --merchant OpenCLI Test Merchant \ --category Office Supplies Equipment \ --notes OpenCLI adapter smoke test; do not submit. \ -f json通过条件Mercury 停在 Review 且返回行中submitBlocked: true。冒烟测试期间绝不要点击最终的 Submit也不要在生产工作区/真实票据上执行。此外仓库自带完整的 vitest 测试套件 clis/mercury/mercury.test.js覆盖输入校验、reimbursement-plan确定性输出、reimbursement-draft的 12 类失败路径未登录、畸形状态、已有审核面、上传漂移、字段缺失、未达 Review 等与成功路径是理解适配器行为边界的最佳参考。设计约束与注意事项原文档在 Notes 一节明确了五条不可忽略的设计约束每条都可以在源码中找到对应实现登录是硬性前提适配器复用 OpenCLI 选中浏览器 profile 的登录态不存储 Mercury 凭据、不执行 OAuth/登录流程未登录直接抛AuthRequiredError票据上传依赖固定选择器[data-testidexpense-attachment-upload]是 Mercury 附件输入的唯一锚点。若 Mercury 修改该选择器上传会失败但表单其余部分仍然可见因此故障表现是上传失败而表单正常OCR 先于纠正执行Mercury OCR 可能误读币种例如把CNY读成JPY或覆盖商户/金额。命令先上传票据、等待、再用 CLI 参数重新填充字段这正是--ocr-wait-seconds存在的意义Review 不等于提交reimbursement-draft永不按下最终的Submit expense按钮只准备一份待人工检查的草稿使用原始币种应录入票据上的原始币种Mercury 支持时然后在 Review 页核对 Mercury 换算后的报销金额输出刻意精简返回行只是控制面control-plane状态摘要最终视觉审核以 Mercury 页面为准。故障排查指南原文档给出的排查表结合源码可以补充更精确的判定依据needs_login在同一个 Chrome/OpenCLI profile 中打开 Mercury 完成登录再重跑check-login。对应源码路径是assertLoggedIn抛出的AuthRequiredError上传失败先确认本机文件真实存在再确认 Mercury 仍使用[data-testidexpense-attachment-upload]作为票据输入。该命令要求 Browser BridgeuploadFiles支持以便验证目标文件输入若后端无文件注入能力会在上传前直接失败Review 失败打开浏览器检查 Mercury 的校验错误——命令可能已上传票据并填写字段但未能到达 Review 步骤。失败抛出的CommandExecutionError提示Mercury Review button was not clicked; inspect the page for validation errorsreimbursement-draft.js分类未生效Category did not commitMercury 的自定义下拉对 UI 变化敏感重试时使用 Mercury 页面中精确可见的分类标签文本。源码中 category 字段会额外派发Enter键事件以触发下拉确认UI 变化时这一路径最易受影响。小结OpenCLI 的 Mercury 适配器是一个无 API 场景下安全写入的典型范本本地校验先行reimbursement-plan、登录态探测check-login、带后置条件的 UI 写入reimbursement-draft以及贯穿始终的绝不点击最终提交安全边界。对 Agent 而言正确的使用姿势是让reimbursement-draft停在 Review 页把最终提交决定权留给人类或负责任的监督者——这既是适配器的行为约定也是其源码与测试共同强化的契约。赞分享开发工具CLI人工智能AI 应用浏览器控制GUI 自动化【免费下载链接】OpenCLIMake Any Website into CLI Use your logged-in browser by AI agent.项目地址https://gitcode.com/gh_mirrors/ope/OpenCLI点击查看免费下载相关推荐OpenCLI Bilibili 浏览器适配器实战指南用登录态浏览器驱动 B 站数据读写OpenCLI Bilibili 浏览器适配器实战指南用登录态浏览器驱动 B 站数据读写 OpenCLI 的 Bilibili 适配器以“浏览器模式”开发工具CLI人工智能AI 应用浏览器控制GUI 自动化OpenCLI Claude 适配器实战用命令行驱动 claude.ai 浏览器会话OpenCLI Claude 适配器实战用命令行驱动 claude.ai 浏览器会话 本指南聚焦 OpenCLI 的 claude 浏览器适配器Browse开发工具CLI人工智能AI 应用浏览器控制GUI 自动化OpenCLI DeepSeek 适配器实战用浏览器会话驱动 chat.deepseek.com 的 CLI 命令指南OpenCLI DeepSeek 适配器实战用浏览器会话驱动 chat.deepseek.com 的 CLI 命令指南 导读 本文讲解 OpenCLI 项目中开发工具CLI人工智能AI 应用浏览器控制GUI 自动化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考