news 2026/9/19 22:39:20

OpenCLI Grok 适配器实战:用浏览器会话在终端驱动 grok.com 聊天、图像生成与对话导出

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenCLI Grok 适配器实战:用浏览器会话在终端驱动 grok.com 聊天、图像生成与对话导出

OpenCLI Grok 适配器实战:用浏览器会话在终端驱动 grok.com 聊天、图像生成与对话导出

【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI

本指南以 OpenCLI 仓库中 grok 适配器文档 为主体,完整讲解如何通过opencli grok系列命令在终端驱动 grok.com 的聊天、历史阅读、对话导出、图像生成与会话管理。所有命令都复用你已登录的 Chrome 浏览器会话,无需 API Key;读完本文,你将掌握每个命令的参数、输出列、底层实现原理与异常处理策略,能够直接在 Agent 工作流中集成 Grok。

模式与原理:浏览器适配器(Browser Adapter)

Grok 适配器属于🔐 Browser 模式,作用于grok.com域。与需要 API Key 的云端适配器不同,浏览器适配器的核心思路是:CLI 通过 Browser Bridge 扩展 连接到你的 Chrome 实例,在已登录的 grok.com 页面上下文中执行 JavaScript,从而驱动真实页面完成交互。

整个链路为:opencli(Node.js)↔ WebSocket(localhost:19825)↔ micro-daemon(自动启动)↔ Chrome 扩展 ↔ grok.com 页面。浏览器扩展在网页上下文中执行脚本,天然携带登录 Cookie,因此不需要任何令牌或手动配置。

从源码结构看,适配器由 clis/grok 目录下的 13 个命令文件加上一个共享工具模块 clis/grok/utils.js 组成:

  • statushistoryreaddetailexportexport-all为只读操作(access:read);
  • asksendnewimagepinunpindelete为写入操作(access:write);
  • 所有命令都声明strategy: Strategy.COOKIEbrowser: truesiteSession: 'persistent'(详见各命令源码,如 ask.js),意味着命令默认复用同一个持续会话的 Grok 页面,连续调用ask/read/detail会停留在同一页面上下文。

前置条件

在运行任何opencli grok命令前,请确认:

  1. Chrome 正在运行
  2. 已登录 grok.com(在 Chrome 中完成登录);
  3. Browser Bridge 扩展 已安装

Browser Bridge 的安装非常简单:在chrome://extensions开启开发者模式后,加载extension/目录(源码方式)或官方 Release 的 zip 包即可。装好后无需任何配置,daemon 会在首次执行浏览器命令时自动启动,可用opencli doctor检查扩展与 daemon 的连接状态(详见 docs/guide/browser-bridge.md)。

命令总览

下表是opencli grok的完整命令清单、功能与访问级别(写操作会改变 Grok 账户数据,只读操作仅读取页面):

CommandDescriptionAccess
opencli grok statusPage availability, login state, current model and sessionread
opencli grok historyList recent conversations from the sidebar (requires login)read
opencli grok readRead messages in the current conversationread
opencli grok detail <id>Open a conversation by ID and read its messagesread
opencli grok exportExport visible conversation history metadata from the history dialogread
opencli grok export-allExport conversation metadata plus per-conversation transcript JSONread
opencli grok ask <prompt>Send a prompt and wait for the assistant replywrite
opencli grok send <prompt>Fire-and-forget: send a prompt without waitingwrite
opencli grok newStart a fresh conversationwrite
opencli grok image <prompt>Generate images via Grok and return their URLswrite
opencli grok pin <id>Pin a conversation from the sidebar context menuwrite
opencli grok unpin <id>Unpin a conversation from the sidebar context menuwrite
opencli grok delete <id> --yesDelete a sidebar conversation after an explicit--yesconfirmationwrite

会话与状态检查:status / new / history

status:最快的健康检查

opencli grok status返回页面可用性、登录状态、当前模型与会话 ID,是所有 Grok 命令的"体检入口":

opencli grok status

输出列:Status, Login, Model, SessionId, Url

底层实现位于 clis/grok/status.js:先ensureOnGrok(page)确保停留在 grok.com,然后并行执行登录检测、会话 ID 提取、模型标签提取。其中登录检测逻辑(utils.js)以输入框(composer)是否存在作为登录标志——登出状态下 grok.com 渲染的是"Sign in"引导按钮而非 TipTap 编辑器;模型标签则优先使用稳定的#model-select-trigger选择器而非随浏览器语言变化的 aria-label(utils.js)。

需要注意:当模型或会话 ID 无法检测时(如页面仍在加载),status返回的Model/SessionIdnull而不是字符串哨兵值(源码中明确Model: model ? model : null),Agent 代码中应针对null做分支判断。

new:开启新对话

opencli grok new

输出列:Status。底层实现很简洁(new.js):Grok 当前 UI 没有独立的"新建对话"按钮,startNewChat的做法是直接导航回https://grok.com/首页(utils.js),因此该命令等价于"回到首页开始新会话"。

history:从侧边栏列出最近对话

opencli grok history --limit 10

输出列:Index, Title, Url(Url 为https://grok.com/c/<id>形式)。--limit默认 20、最大 100。需要登录,未登录抛AuthRequiredError

底层逻辑(history.js + utils.js)扫描侧边栏中所有a[href^="/c/"]可见链接,按 UUID 去重后提取标题;针对侧边栏中"标题链接"与"图标链接"(图标链接无文本)的 DOM 顺序问题,采用两遍扫描:先收集首个非空标题,再用第二轮为同 ID 的空标题回填。注意history读取的是当前侧边栏可见部分——如果 Grok 懒加载了更早的对话,需要先在浏览器里滚动侧边栏,或直接用detail <id>

limit的校验是硬性的:非正整数或超过上限会直接抛出ArgumentErrormust be a positive integer/must be <= 100),不会静默截断。

阅读对话:read 与 detail

read:读取当前对话

# 以 markdown 输出助手回复 opencli grok read --markdown true

输出列:Role, Textread读取当前标签页正在显示的对话,即使不传 ID 也能工作;配合status先拿到当前 SessionId 是推荐用法。若页面没有可见消息,返回一行Role: system, Text: No visible messages in the current conversation.(read.js)。

detail:按 ID 打开历史对话

opencli grok detail 7c4197f2-10a1-4ebb-a84a-fea89f4f1d06 opencli grok detail https://grok.com/c/7c4197f2-10a1-4ebb-a84a-fea89f4f1d06 --markdown true

输出列:Role, Textdetail会导航到https://grok.com/c/<id>并轮询等待对话记录加载(最长约 20 秒,见 detail.js),避免页面壳先渲染而消息后加载导致的竞态;如果轮询结束仍无可见消息,抛出EmptyResultError,提示核对 ID 是否属于当前登录账户。

消息读取的底层实现

read/detail都基于getMessageBubbles(utils.js):

  • 通过[data-testid="user-message"]/[data-testid="assistant-message"]区分角色;
  • 向上查找最近的id="response-<uuid>"祖先作为每条消息的稳定 ID(用于轮询去重);
  • 保留"仅含图片/非文本组件"的助手回合(text 为空但 html 非空也会保留),避免图回合被静默丢弃;
  • --markdown true时,助手回复的 HTML 通过bubbleHtmlToMarkdown(utils.js)转为 Markdown。

对应的单元测试见 clis/grok/utils.test.js,覆盖了会话 ID 解析、域名判断、布尔参数归一化、置顶状态识别等核心函数。

会话 ID 与 URL 解析规则

detailpinunpindelete都接受会话 ID 参数,统一由parseGrokSessionId(utils.js)解析,规则如下:

  • 裸 UUID(7c4197f2-10a1-4ebb-a84a-fea89f4f1d06)直接接受;
  • 完整 URL(https://grok.com/c/<id>或子域https://x.grok.com/c/<id>)提取其中的 UUID;
  • 只接受https:协议与grok.com/*.grok.com主机名,路径必须精确匹配/c/<uuid>
  • 校验失败抛出ArgumentError,并附期望格式提示;URL 尾部拼接额外十六进制字符(如/c/<id>0)会被拒绝,防止"静默截断打开错误会话"。

utils.test.js 对此有非常完整的测试矩阵,包括大小写归一化、evil.com伪装域名、http:明文协议、路径多余片段等边界情形。

发送消息:ask 与 send

ask:发送并等待回复

opencli grok ask "Explain quantum computing in simple terms" opencli grok ask "Hello" --new true

输出列:response(仅返回助手最新回复文本)。--timeout控制最长等待秒数(默认 120),--new指定是否先开启新会话(默认false)。

ask的完整调用链(ask.js):

  1. --newstartNewChat导航回首页;否则ensureOnGrok确保停留在 grok.com;
  2. isLoggedIn校验登录,未登录抛AuthRequiredError
  3. getBaselineLastAssistantId记录发送前最后一条助手消息 ID(防止把旧回复当新回复);
  4. sendMessage注入提示词并触发提交;
  5. waitForAnswer轮询等待流式回复稳定

send:fire-and-forget

opencli grok send "continue the previous answer"

输出列:Status, Promptsend只负责把提示词发出去、确认已提交(返回Status: sent),不等待回复(send.js);若发送时发现 composer 缺失且登录态已失效,会抛AuthRequiredError而非笼统的执行失败,方便 Agent 触发重新认证。

发送与等待的底层原理

sendMessage(utils.js)优先使用 ProseMirror 编辑器的editor.commands.insertContent注入文本,提交按钮优先使用语言无关button[data-testid="chat-submit"],回退到aria-label("Submit" / "提交" / "送信");若提交按钮始终未出现(某些地区部署无提交按钮,源码注释引用 issue #1782),则退化为对聚焦的编辑器派发 Enter 键(keydown/keypress/keyup 完整链)。提交后通过"出现新的匹配用户回合"来确认发送成功,而不是盲目信任点击。

waitForAnswer(utils.js)采用"稳定性判定"策略:每 2 秒轮询一次,跳过与发送前基线相同的旧助手回合,只有当回复文本连续两次轮询保持一致(且已等待至少 6 秒)才判定为稳定并返回ok;超时但已有部分内容时返回partial(保留最佳努力文本,不丢弃);完全无内容才抛TimeoutError

导出对话:export 与 export-all

export:导出历史元数据

opencli grok export --limit 25 -f json > grok-history.json

输出列:index, id, title, date, url--limit默认 0(0 表示全部已加载的历史),--maxScrolls默认 80、最大 500。

底层实现(export.js)会打开完整的"全部历史"对话框(自动点击"查看全部 / Show all / View all"按钮),然后在对话框滚动容器内反复滚动到底并采集a[href^="/c/"]链接,直到连续多轮行数与滚动高度不再变化(判定稳定)或达到maxScrolls上限;每条记录提取titledate与真实的https://grok.com/c/<id>URL。若出现登录失效(检测到 Sign in CTA 且无历史入口)返回AUTH错误码,对话框未打开返回NO_DIALOG错误码;畸形行会被视为选择器漂移而报错,绝不静默丢弃(由normalizeConversationRows严格校验,见 export-utils.js)。

export-all:导出元数据 + 逐会话完整转录

opencli grok export-all --manifestPath grok-history.json --limit 25 -f json > grok-transcripts.json

输出列:index, id, title, date, url, status, messageCount, error, messagesJson

export-allexport的基础上,逐个访问每个会话的/c/<id>页面并导出完整消息转录,每行额外包含:

  • statusok/empty/failed
  • messageCount:该会话可见消息数;
  • error:失败原因(如"页面加载后无可见消息"、"读取器返回畸形行");
  • messagesJson:转录的 JSON 字符串(每条含messageIndexmessageIdmessageRolemessageText)。

关键参数(export-all.js):

OptionDescription
--limitMax conversations to export;0means all loaded history or all manifest rows after offset
--offsetSkip this many conversations before exporting (default:0)
--manifestPathOptional JSON output fromgrok export; when present, skips history discovery and visits listed/c/<id>pages
--maxScrollsMax history-dialog scroll rounds when no manifest is provided (default:80, max500)
--pageScrollsMax per-conversation scroll-to-bottom rounds (default:30, max200)
--pageTimeoutMsMax wait for each conversation page to show messages (default:30000)
--delayMinMsMinimum polite delay after a conversation page loads (default:0)
--delayMaxMsMaximum polite delay after a conversation page loads (default:5000)

工作方式:若提供--manifestPath,直接从grok export生成的 JSON 清单读取会话列表(跳过历史对话框发现过程),按offset/limit切片后逐个访问/c/<id>页面;否则先执行与export相同的历史对话框滚动采集。每个会话页面:等待消息气泡出现(pageTimeoutMs)、加载后随机礼貌延迟(delayMinMs~delayMaxMs)、滚动到底部采集全部消息(pageScrolls轮,连续 5 轮稳定即止),最后严格校验每行转录结构。页面无可见转录产出empty行,转录行畸形产出failed行,而畸形历史行或清单会在导出前直接失败(不会产生残缺导出)。参数校验同样硬性:delayMaxMs必须>= delayMinMs,各整数参数越界抛ArgumentError

图像生成:image

opencli grok image "a cyberpunk mechanical owl, neon purple and blue" --new true

输出列:url, width, height, path。额外参数:--timeout(默认 240 秒)、--new(默认false)、--count(返回前至少等待的图片数,默认 1)、--out(下载保存目录)。

完整流程(image.js):

  1. 记录发送前的图片气泡基线数,避免把旧图当新图;
  2. 通过 composer 发送提示词(优先 ProseMirror,回退 textarea);
  3. 每 3 秒轮询消息气泡中的<img>元素(过滤小于 128px 的头像/UI 小图、按 src 去重),用"连续两次稳定签名(约 6 秒)"判定生成完成;
  4. 超时但有部分图片时保留最佳努力结果;完全无图抛TimeoutError

下载机制值得注意:当传入--out时,图片是通过浏览器页面的fetch(携带credentials: 'include'与 grok.com referer)以 base64 拉取再写入本地文件的(image.js)。这是因为 assets.grok.com 由 Cloudflare 防护,直接 curl/node 下载会被拒绝;利用浏览器会话即可绕过。下载失败会响亮地抛出CommandExecutionError(而非输出[DOWNLOAD FAILED]哨兵路径),文件名形如grok-<timestamp>-<sha1前12位>.<ext>

会话管理:pin / unpin / delete

pin / unpin

opencli grok pin 7c4197f2-10a1-4ebb-a84a-fea89f4f1d06 opencli grok unpin https://grok.com/c/7c4197f2-10a1-4ebb-a84a-fea89f4f1d06

输出列:status, id。这两个命令(pin.js)通过共享的defineToggle工厂注册:Grok 上下文菜单根据当前置顶状态只显示"置顶"或"取消置顶"之一(不会同时出现),因此两个命令各自绑定匹配的本地化菜单项。执行逻辑为:先读取右键菜单标签判断当前状态,若已处于目标状态则幂等返回already-pinned/already-unpinned;否则右键打开上下文菜单点击目标项,再轮询验证菜单标签确实翻转waitForConversationPinState),验证失败会抛出包含可用菜单标签的执行错误。

delete(带 dry-run 保护)

# 预览(dry-run) opencli grok delete 7c4197f2-10a1-4ebb-a84a-fea89f4f1d06 # 真正删除 opencli grok delete 7c4197f2-10a1-4ebb-a84a-fea89f4f1d06 --yes true

输出列:status, id。删除操作是危险操作:Grok 的删除是立即生效、无确认弹窗的(源码注释明确记录这一点),因此delete默认只是 dry-run 预览(返回status: 'dry-run (pass --yes to actually delete)'),必须显式传入--yes才真正删除。真正删除后还会轮询确认侧边栏条目确实消失才返回deletedwaitForConversationToDisappear),否则抛执行错误。

右键菜单模拟的底层细节

pin/unpin/delete依赖clickConversationMenuItem(utils.js),其中有一个值得注意的实现细节:Grok 的上下文菜单由 radix 实现,只派发contextmenu事件会被忽略,因此代码完整派发了pointerdown → mousedown → contextmenu → pointerup → mouseup事件链(带button: 2右键状态),并在目标链接的边界矩形内选择命中坐标。菜单项匹配支持中英文等多语言标签("置顶/Pin"、"取消置顶/Unpin"、"删除/Delete"、"打开新标签页/Open in new tab"、"重命名/Rename")。

输出列速查表

CommandColumns
statusStatus, Login, Model, SessionId, Url
historyIndex, Title, Url
readRole, Text
detailRole, Text
exportindex, id, title, date, url
export-allindex, id, title, date, url, status, messageCount, error, messagesJson
askresponse
sendStatus, Prompt
newStatus
pinstatus, id
unpinstatus, id
deletestatus, id

完整选项参考

ask/send

OptionDescription
promptPrompt to send (required positional)
--newStart a new chat before sending (default:false)
--timeout(askonly) Max seconds to wait for the reply (default:120)

read

OptionDescription
--markdownEmit assistant replies as markdown (default:false)

detail

OptionDescription
idSession ID (UUID) or fullhttps://grok.com/c/<id>URL (required positional)
--markdownEmit assistant replies as markdown (default:false)

history

OptionDescription
--limitMax conversations to list (default:20, max100)

export

OptionDescription
--limitMax conversations to export;0means all loaded history (default:0)
--maxScrollsMax history-dialog scroll rounds (default:80, max500)

export-all

OptionDescription
--limitMax conversations to export;0means all loaded history or all manifest rows after offset
--offsetSkip this many conversations before exporting (default:0)
--manifestPathOptional JSON output fromgrok export; when present, skips history discovery and visits listed/c/<id>pages
--maxScrollsMax history-dialog scroll rounds when no manifest is provided (default:80, max500)
--pageScrollsMax per-conversation scroll-to-bottom rounds (default:30, max200)
--pageTimeoutMsMax wait for each conversation page to show messages (default:30000)
--delayMinMsMinimum polite delay after a conversation page loads (default:0)
--delayMaxMsMaximum polite delay after a conversation page loads (default:5000)

pin/unpin/delete

OptionDescription
idSession ID (UUID) or fullhttps://grok.com/c/<id>URL (required positional)
--yes(deleteonly) Actually delete the conversation; without it the command returns a dry-run row

实战注意事项与陷阱

  • read无 ID 也能用:它读取当前标签页的对话;组合用法是先用status获取 SessionId,再决定read还是detail
  • 会话持续性:Grok 命令默认使用持久站点会话,连续ask/read/detail会在同一个 Grok 页面内继续;需要一次性标签页时传--site-session ephemeral
  • askvssendask等流式回复稳定后返回;send提交即返回。
  • history受侧边栏可见性限制:Grok 懒加载旧对话时,先在浏览器里滚动侧边栏,或直接用detail <id>
  • export的严格性:畸形历史行按选择器漂移处理并报错,不静默丢弃,保证导出的 URL 全部真实可访问。
  • export-all的行状态:无可见转录的会话产生empty行(带error),畸形转录行产生failed行,Agent 应消费status列做重试或跳过。
  • pin/unpin/delete的验证闭环:三个命令都要求操作"可见于侧边栏"的会话,并在操作后验证上下文菜单状态(pin/unpin)或侧边栏条目消失(delete)才返回成功。
  • null而非哨兵字符串statusModel/SessionId检测不到时是null,Agent 代码必须对null分支。
  • DOM 漂移的应对:Grok 产品改版可能导致 composer 检测失败,opencli grok status是最快的体检手段。
  • 参数校验不静默limit等参数非法(非正数或超过文档上限,如history最大 100)会直接抛ArgumentError,不会悄悄截断。

延伸阅读

  • 适配器权威文档:docs/adapters/browser/grok.md
  • 核心共享实现:clis/grok/utils.js(会话 ID 解析、登录检测、消息气泡读取、发送与等待、右键菜单模拟)
  • 各命令实现:ask.js、read.js、detail.js、history.js、export.js、export-all.js、image.js、pin.js、delete.js、send.js、new.js、status.js
  • 导出行校验工具:clis/grok/export-utils.js
  • 单元测试:clis/grok/utils.test.js、clis/grok/ask.test.js、clis/grok/export.test.js、clis/grok/image.test.ts
  • 浏览器桥接与会话管理:docs/guide/browser-bridge.md

【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 22:37:02

同一把 TaoToken Key,Cursor 从 GPT-4 切到 Claude

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 22:34:05

BrewUI体验:为Homebrew包管理打造的可视化仪表盘

说实话&#xff0c;最早看到BrewUI这个项目的时候&#xff0c;我内心是有点不屑的。Homebrew这套包管理工具&#xff0c;从入行第一天就是跟终端打交道的&#xff0c;brew install、brew upgrade、brew list这些命令早就在我肌肉记忆里了&#xff0c;一个命令能解决的事为什么要…

作者头像 李华
网站建设 2026/9/19 22:28:26

CPU大核闲置?从调度原理到强制绑核的完整实操指南

先说一个很多人的困惑&#xff1a;明明换了新平台、买了一大堆高性能核心&#xff0c;结果某个程序还是卡得不行。打开任务管理器一看&#xff0c;CPU总占用率不高&#xff0c;大核有一多半是闲着的&#xff0c;反而是低功耗小核心上跑满了线程。这类问题在Intel 12代/13代/14代…

作者头像 李华