OpenCLI doubao 浏览器适配器实战指南:用命令行与 AI Agent 驱动豆包网页版对话
【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
豆包(Doubao Chat)官方并未提供面向开发者与 AI Agent 的稳定 CLI 接口,而 OpenCLI 的doubao浏览器适配器通过复用它已登录的 Chrome 会话,把豆包网页版(https://www.doubao.com/chat)封装成一组完整的命令行工具。本文以 docs/adapters/browser/doubao.md 为骨架,结合 clis/doubao 目录下的真实源码实现,讲解status、new、send、read、ask、detail、history、meeting-summary、meeting-transcript九个命令的用法、会话机制与底层 DOM 自动化原理,读完后你可以直接用命令行驱动豆包完成对话问答、历史回放与会议纪要提取。
一、前置条件:浏览器桥接与登录态
在使用任何doubao命令之前,需要满足以下三个前提(原文档明确列出):
- Chrome 正在运行:适配器不启动新浏览器,而是接管已运行的 Chrome 实例;
- 已在 doubao.com 完成登录:登录态以 Cookie 形式存在;
- 已为 OpenCLI 安装并启用 Browser Bridge 扩展:这是命令与页面之间通信的桥梁。
关于登录态的判定,源码中有更精确的实现。在 clis/doubao/auth.js 中,hasDoubaoSessionCookie检查https://www.doubao.com域下是否存在名为passport_csrf_token的 Cookie;随后verifyDoubaoIdentity会在当前页面内通过 fetch 请求/passport/account/info/v2/接口,只有当响应中存在user_id_str时才判定为真正登录成功。值得注意的是,源码注释特别说明passport_csrf_token在匿名会话中也会被设置,因此认证流程不会仅凭 Cookie 存在就放行,而是以账户 API 返回的真实user_id为准,避免登录中途误判。
二、命令总览
原文档给出了完整的命令清单,下表在原文基础上补充了各命令的访问权限(read/write)与核心输出字段:
| 命令 | 描述 | 权限 | 主要输出 |
|---|---|---|---|
opencli doubao status | 检查页面是否可达、豆包是否已登录 | read | Status / Login / Url / Title |
opencli doubao new | 开始一个新的豆包对话 | read | Status / Action |
opencli doubao send "..." | 向当前豆包对话发送一条消息 | write | Status / SubmittedBy / InjectedText |
opencli doubao read | 读取当前可见的豆包对话 | read | Role / Text |
opencli doubao ask "..." | 发送提示词并等待回复 | write | Role / Text |
opencli doubao detail <id> | 对话详情 | read | Role / Text |
opencli doubao history | 历史对话列表 | read | Index / Id / Title / Url |
opencli doubao meeting-summary <id> | 会议总结(可附加章节) | read | Section / Content |
opencli doubao meeting-transcript <id> | 会议记录(文本或下载) | read | Section / Content |
所有命令在 clis/doubao 目录中都有对应实现文件,例如status.js、send.js、ask.js等。从源码看,每个命令都通过@jackwener/opencli/registry的cli()注册,统一声明domain: 'www.doubao.com'、strategy: Strategy.COOKIE、browser: true与siteSession: 'persistent',说明整个适配器采用"复用浏览器 Cookie 会话"的策略,而非账号密码登录。
三、常用命令实操示例
原文档给出了以下可直接运行的示例:
opencli doubao status opencli doubao new opencli doubao send "帮我总结这段文档" opencli doubao read opencli doubao ask "请写一个 Python 快速排序示例" --timeout 90各命令的细节与参数说明如下。
1. status:探测页面与登录态
status会跳转到豆包聊天页并读取页面状态。status.js 内部调用getDoubaoPageState,通过注入页面的getStateScript读取window._ROUTER_DATA?.loaderData?.chat_layout中的userSetting.data.is_login字段、当前 URL、页面标题和输入框 placeholder。返回结果中Login可能是Yes/No/Unknown(当页面数据尚未加载完成时为 Unknown),Status相应显示Connected或Login Required。
2. new:开启新对话
new命令先尝试点击页面上的 "新对话 / New Chat" 按钮,找不到时再回退到新建会话路由。这个逻辑在 utils.js 的clickNewChatScript中实现:它遍历页面中所有button, a, [role="button"]元素,匹配['新对话', 'New Chat', '创建新对话']标签并点击;若全部失败,则通过page.goto(DOUBAO_NEW_CHAT_URL)(https://www.doubao.com/chat/new-thread/create-by-msg)导航到新建会话路由。new.js 会把实际执行方式(点击了哪个按钮,还是回退导航)写入Action列返回。
3. send:发送消息
send只负责把消息送入输入框并提交,不等待回复。从 send.js 看,它调用sendDoubaoMessage后返回Status、SubmittedBy(通过按钮点击还是回车提交)与InjectedText三个字段。
sendDoubaoMessage是发送链路的核心,其完整流程(utils.js)为:
- 确保处于聊天页:
ensureDoubaoChatPage先检查当前 URL 是否包含doubao.com/chat,否则按评分(对话详情页 > 首页 > 活跃标签页)优先复用已存在的豆包标签页,最后才goto到聊天首页; - 定位输入框:
buildDoubaoComposerLocatorScript维护了一组从强到弱的 CSS 选择器(textarea[data-testid="chat_input_input"]→[data-testid="chat_input"] textarea→.chat-input→[contenteditable="true"]等),并逐一过滤掉不可见元素; - 优先原生键入:若浏览器桥提供
page.nativeType,优先用原生输入模拟真实键盘事件,随后同步触发beforeinput/input/change事件让 React 状态感知输入; - 回退到脚本填充:若原生输入后文本与预期不一致,改用
fillComposerScript,通过HTMLTextAreaElement.prototype的 value setter 注入文本,或在 contenteditable 元素上用document.execCommand('insertText')填充; - 提交消息:先尝试
clickSendButtonScript智能点击发送按钮,失败则按 Enter 键。按钮识别采用评分制:优先匹配文案/aria-label/title 中含send|发送|提交|发消息的按钮,排除"新对话、视频生成、深入研究、图像生成、上传、麦克风"等干扰按钮,并结合按钮与输入框的几何距离、CSS 类名(如bg-dbx-text-highlight)加权打分,得分低于 200 视为未找到可靠按钮; - 验证码检测:提交后运行
detectDoubaoVerificationScript,若检测到 captcha iframe、验证码输入框,或对话框文案匹配人机验证|完成安全验证|异常访问|滑动验证|拖动滑块等模式,则抛出CommandExecutionError,提示用户手动完成验证。
4. read:读取当前对话
read读取当前页面可见的对话轮次(turns),输出Role(User / Assistant)与Text。其底层getTurnsScript(utils.js)实现了一套完整的角色识别逻辑:
- 通过
[data-testid="send_message"]、[class*="bg-g-send-msg-bubble"]等标记识别用户消息; - 通过
[data-testid="receive_message"]、[class*="bg-g-receive-msg-bubble"]等标记识别助手消息; - 源码注释表明,2026 年 5 月豆包 DOM 重构后,助手消息不再带
receive-message标记,改为通过[class*="inner-item-"]/[class*="top-item-"]外层容器配合.flow-markdown-body/.md-box-root内容容器、且不含发送气泡标记来推断 Assistant 角色; - 提取文本时对多个选择器依次尝试,去重合并,并附带过滤 48px 以下的小图标;消息按 DOM 文档顺序排序,最终以
Role::Text为键去重。
如果页面中一个可见消息都没有,read.js 会返回No visible Doubao messages were found.。
5. ask:一问一答的完整闭环
ask是自动化场景最常用的命令:发送提示词后轮询等待回复。ask.js 声明了两个参数:位置参数text(必填)与--timeout(可选,默认60 秒,必须是正整数)。
其执行流程(ask.js):
- 记录发送前已有的可见轮次与转录行;
- 调用
sendDoubaoMessage发送消息; - 调用
waitForDoubaoResponse以2 秒为间隔轮询页面,每次轮询都会先做验证码检测,然后找出"发送后新出现的 Assistant 轮次"作为候选回复;若可见轮次尚无内容,则对比发送前后的转录行增量,过滤掉提示词本身、内容由豆包 AI 生成、在此处拖放文件、window._SSR_DATA、{"namedChunks"等 UI 噪音; - 当同一候选内容连续2 次轮询保持不变(即内容稳定不再增长),或达到超时上限时,判定生成完成;
- 若超时仍未拿到回复,返回
No response within {timeout}s. Doubao may still be generating.,此时可以调大--timeout重试。
由于回复依赖 DOM 轮询而非接口,超长生成任务需要更大的--timeout,这正是原文档 Notes 中特别提醒的一点。
6. history:历史对话列表
history从豆包侧边栏读取历史会话,可选参数--limit(默认 50)。utils.js 的getConversationListScript定位[data-testid="flow_chat_sidebar"]侧边栏,抓取其中所有a[data-testid="chat_list_thread_item"]链接,通过正则\/chat\/(\d{10,})提取会话 ID 与标题,输出Index / Id / Title / Url四列。history.js 在未登录或侧边栏为空时给出明确提示。
7. detail:按 ID 读取指定对话
detail <id>接受数字 ID 或完整 URL,parseDoubaoConversationId 会从输入中提取\d{10,}数字作为会话 ID。随后navigateToConversation跳转到https://www.doubao.com/chat/{id}并等待加载,getConversationDetailScript(utils.js)从[data-testid="message-list"]中提取union_message节点,按是否含send_message/receive_message标记区分角色,同时识别meeting-minutes-card会议卡片。detail.js 会把会议卡片信息以Role: Meeting的形式展示在消息列表之前。
8. meeting-summary:会议总结与章节
meeting-summary <id>用于提取豆包会议会话的总结,可选参数--chapters true一并输出 AI 章节。执行时(meeting-summary.js)先定位并点击[data-testid="meeting-minutes-card"]会议卡片打开右侧面板(canvas_panel_container),再从[data-testid="meeting-summary-todos"]读取总结;若请求章节,则切换到包含"章节"文字的 tab 读取[data-testid="meeting-ai-chapter"]内容。若对话中不存在会议卡片,返回No meeting card found in this conversation.。
9. meeting-transcript:会议记录读取与下载
meeting-transcript <id>可选参数--download true。默认模式下(meeting-transcript.js)它会打开会议面板、切换到包含"文字"的 tab,然后对[data-testid="meeting-text-notes"]区域滚动到底并反复快照,最多 10 轮,直到连续 2 轮内容稳定;快照通过 mergeTranscriptSnapshots 做基于行重叠的去重合并(利用"最长重叠后缀 + 新内容追加"算法拼接滚动产生的分段文本)。--download true模式则改为点击下载图标与[data-testid="minutes-download-text-btn"]按钮,触发浏览器文件下载,提示用户去 Downloads 目录取文件。
四、会话模型:持久站点会话与一次性标签页
原文档 Notes 中强调了豆包适配器的会话特性:豆包命令默认使用持久站点会话(persistent site session),因此连续执行doubao ask/doubao read/doubao detail会在同一个豆包页面中继续,上下文得以保留。
若想每次命令使用一次性标签页、互不干扰,可以追加参数:
opencli doubao ask "..." --site-session ephemeral--site-session ephemeral会让命令在一个临时标签页中执行、结束后关闭,适合需要隔离上下文的批处理场景;默认的persistent模式则适合连续的对话式操作。该行为与源码中每个命令的siteSession: 'persistent'声明一致(见 status.js、ask.js 等)。
五、底层原理与抗变化设计
结合源码,豆包适配器之所以能在前端频繁改版下保持可用,主要依赖以下几层设计:
- 多级选择器兜底:无论是输入框定位(composer)、消息角色识别还是消息文本提取,都维护了一组从"精确 contenteditable="false">【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.
项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考