using-grok-bot-app
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUsing the Grok Bot app
使用Grok Bot应用
/Applications/Grok Bot.appagent-browserIdentity caveat. The bundle identifier is (Anysphere,
the Cursor vendor), not xAI. The name is a costume. Do not infer xAI API
semantics, model names, or rate limits from it.
com.anysphere.sandName collision. in this fleet is a Tailscale deploy host, a
completely different thing. If the task is SSH, deployment, or port 5173, this
skill is the wrong one.
box@grok-bot/Applications/Grok Bot.appagent-browser身份注意事项:包标识符为(Anysphere是Cursor的开发商),并非xAI。这个名字只是个外壳。不要据此推断xAI API的语义、模型名称或速率限制。
com.anysphere.sand名称冲突:本集群中的是一台Tailscale部署主机,完全是两回事。如果任务涉及SSH、部署或5173端口,请勿使用此技能。
box@grok-botConnect
连接
The flag only takes effect at launch, so an
already-running instance has no port to attach to and must be restarted. That
restart drops whatever is typed in the composer, which is why it needs the
user's OK before you pull the trigger.
--remote-debugging-portsh
pgrep -xl "Grok Bot" # running?
lsof -nP -iTCP:9231 -sTCP:LISTEN # already debuggable?
pkill -x "Grok Bot" # only with user approval
open -a "Grok Bot" --args --remote-debugging-port=9231
agent-browser connect 9231
agent-browser tab # expect one target: file://…/app.asar/dist/renderer/index.htmlIf the port is already listening, skip straight to — no restart, no
approval needed, nothing lost.
connect--remote-debugging-portsh
pgrep -xl "Grok Bot" # running?
lsof -nP -iTCP:9231 -sTCP:LISTEN # already debuggable?
pkill -x "Grok Bot" # only with user approval
open -a "Grok Bot" --args --remote-debugging-port=9231
agent-browser connect 9231
agent-browser tab # expect one target: file://…/app.asar/dist/renderer/index.html如果端口已经在监听,直接跳到步骤即可——无需重启,无需批准,也不会丢失任何内容。
connectAddress bots by UUID, never by name
通过UUID而非名称定位机器人
Every sidebar entry is with the human-readable
name in . Names repeat — several bots are literally called —
so an selector silently picks whichever one the DOM happened to
order first. The UUID is the only stable key, and it has survived app upgrades
(observed across 0.29.0 → 0.39.0), so it is safe to remember one between
sessions and resolve it back to a name at run time.
button[data-agent-id="<uuid>"]aria-label座位aria-label侧边栏的每个条目都是,其可读名称存储在属性中。名称可能重复——有好几个机器人就叫——因此使用选择器会悄无声息地选中DOM排序中第一个出现的条目。UUID是唯一稳定的标识,并且在应用升级中保持不变(已在0.29.0 → 0.39.0版本中验证),因此可以在会话间记住UUID,运行时再解析回名称。
button[data-agent-id="<uuid>"]aria-label座位aria-labelRead
读取数据
Take one to see the shape of the tree, then switch to for
everything after that. Snapshots pour the whole accessibility tree into
context; returns exactly the JSON you asked for, which is the difference
between a cheap session and an expensive one.
snapshot -ievalevalBot roster and folder structure, no clicking required:
sh
agent-browser eval '(() => JSON.stringify({
bots: [...document.querySelectorAll("button[data-agent-id]")]
.map(b => ({id: b.getAttribute("data-agent-id").slice(0,8), name: b.getAttribute("aria-label")})),
sidebar: document.querySelector("[aria-label=\"Bot list\"]")?.innerText.replace(/\n+/g,"|").slice(0,800)
}))()'The sidebar text carries each bot's last message preview and timestamp, so
"what did RULES last say" and "which bots moved today" are answerable without
opening anything. Reach for the full transcript only when the preview is not
enough.
Full transcript of one bot — this requires selecting it, which changes what the
user sees on screen. The active bot is marked , so capture
it in the same call that navigates away, and you can restore it afterwards
without guessing. Substitute the target UUID prefix for :
aria-current="page"<uuid8>sh
agent-browser eval '(async () => {
const prev = document.querySelector("button[data-agent-id][aria-current=\"page\"]")
?.getAttribute("data-agent-id");
document.querySelector("button[data-agent-id^=\"<uuid8>\"]").click();
await new Promise(r => setTimeout(r, 2500));
const g = [...document.querySelectorAll("[role=group][aria-label$=\"message\"]")];
return JSON.stringify({prev, count: g.length, msgs: g.slice(-5).map(m =>
m.getAttribute("aria-label") + ": " + (m.innerText||"").replace(/\s+/g," ").slice(0,200))});
})()'Messages live in nodes whose is
or , nested in elements. Slice the text — a long
transcript will otherwise dump tens of thousands of characters into context for
no gain.
[role=group]aria-label"<bot> message""Your message"articlePut the screen back. Selecting a bot is a visible change to the user's app,
so click the captured UUID when you are done, and say that you did.
Restore by UUID rather than by name — names repeat, and there is no reliable
heading to read the state back from ( came back empty in practice).
Confirm with the whole panel's text instead:
prevmain h2sh
agent-browser eval '(async () => {
document.querySelector("button[data-agent-id^=\"<prev8>\"]").click();
await new Promise(r => setTimeout(r, 1500));
return (document.querySelector("main")?.innerText || "").replace(/\s+/g," ").slice(0,80);
})()'先执行一次查看DOM树结构,之后所有操作都切换到。快照会把整个无障碍树都塞进上下文;而只返回你明确请求的JSON,这就是低成本会话和高成本会话的区别。
snapshot -ievaleval无需点击即可获取机器人列表和文件夹结构:
sh
agent-browser eval '(() => JSON.stringify({
bots: [...document.querySelectorAll("button[data-agent-id]")]
.map(b => ({id: b.getAttribute("data-agent-id").slice(0,8), name: b.getAttribute("aria-label")})),
sidebar: document.querySelector("[aria-label=\"Bot list\"]")?.innerText.replace(/\n+/g,"|").slice(0,800)
}))()'侧边栏文本包含每个机器人的最后一条消息预览和时间戳,因此“RULES最后说了什么”和“哪些机器人今天有更新”这类问题不用打开任何对话就能回答。只有当预览信息不足时才需要获取完整对话记录。
获取单个机器人的完整对话记录——这需要选中该机器人,会改变用户屏幕上的应用显示内容。当前活动的机器人标记为,因此在导航离开的同一个调用中捕获当前活动的机器人ID,之后就可以无需猜测地恢复原状。将目标UUID前缀替换为:
aria-current="page"<uuid8>sh
agent-browser eval '(async () => {
const prev = document.querySelector("button[data-agent-id][aria-current=\"page\"]")
?.getAttribute("data-agent-id");
document.querySelector("button[data-agent-id^=\"<uuid8>\"]").click();
await new Promise(r => setTimeout(r, 2500));
const g = [...document.querySelectorAll("[role=group][aria-label$=\"message\"]")];
return JSON.stringify({prev, count: g.length, msgs: g.slice(-5).map(m =>
m.getAttribute("aria-label") + ": " + (m.innerText||"").replace(/\s+/g," ").slice(0,200))});
})()'消息位于节点中,其为或,嵌套在元素内。请对文本做截断——否则很长的对话记录会无缘无故地将数万个字符塞入上下文。
[role=group]aria-label"<bot> message""Your message"article恢复界面原状:选中机器人会对用户的应用界面造成可见更改,因此操作完成后请点击之前捕获的 UUID,并告知用户已恢复界面。请通过UUID而非名称恢复——因为名称可能重复,而且没有可靠的标题可以回读状态(实际测试中返回为空)。可以用整个面板的文本来确认恢复结果:
prevmain h2sh
agent-browser eval '(async () => {
document.querySelector("button[data-agent-id^=\"<prev8>\"]").click();
await new Promise(r => setTimeout(r, 1500));
return (document.querySelector("main")?.innerText || "").replace(/\s+/g," ").slice(0,80);
})()'Sending is gated
发送操作需受限
Typing into the composer works: it is a single with
placeholder , and Enter submits. But a sent message is an irreversible
external side effect — another agent reads it and acts. So draft the text,
show it to the user verbatim, and send only after they approve that text.
div[contenteditable=true]Prompt在输入框中输入是可行的:它是单个元素,占位符为,按Enter键即可提交。但发送消息是不可逆的外部副作用——另一个代理会读取它并采取行动。因此请先起草文本,逐字展示给用户,只有在用户批准该文本后才能发送。
div[contenteditable=true]PromptWhat will bite you
常见坑点
- Element refs go stale. fails with
agent-browser click @e5once React re-renders, becauseCould not locate element with role=button name=…means "the Nth node of that particular snapshot". Re-snapshot immediately before interacting, or bypass refs entirely with@eNand a DOM attribute selector — the latter is what the recipes above do.eval - No local storage to shortcut through. ,
localStorage, andsessionStorageare all empty. The DOM is the only source, so there is no "just query the database" path; a full export costs one click and one wait per bot.indexedDB.databases() - The two message counts disagree. One conversation showed a header of
"3 messages with 2 Bots" while the DOM held 7 nodes. Which one is authoritative, and why they differ, is
[role=group]— the header may count threads rather than messages, or the DOM may hold rendered system entries. Neither number is a safe answer on its own: if a transcript looks short or the counts disagree, scroll theUNCONFIRMEDcontainer, re-read, and report what you actually saw.log "Conversation transcript" - counts lie. One
articlereturned a singleevalwhile the snapshot showed dozens. When dumping a whole transcript, prefer the union selectorarticle.'[role=article],article,[role=group]' - 一趟抓不全,而且捲到頂會刪資料。 對話串是虛擬化清單:一跳到頂,底部節點就被回收,等於邊讀邊刪(一次擷取因此掉了整天份的最新訊息)。 可靠做法是兩種獨立方法各跑一趟再取聯集:錨點法(抓最頂那則
scrollTop = 0,[role=group],直到頂端訊息連續數次不變 且總數不再增長——兩個條件要同時成立,只看一個會把「捲不動」誤判成「到頂」) 與像素法(scrollIntoView({block:"start"})的[role=log][aria-label="Conversation transcript"]由頂往下每次 0.8 屏)。去重鍵用scrollTop,不要用 DOM 節點參照—— 同一則訊息被回收重建後是不同節點,內容才是穩定的身分。 兩法數字一致才算抓全:五個 bot 兩法各自給出 151/151、53/53、38/38、10/10、 7/7,那是可信的完整性證據;另一個 bot 兩法給 277 vs 132、聯集 372,就只能標aria-label + 前 120 字。UNCONFIRMED - 對話串短,先懷疑擷取方法,不要當成「這個 bot 沒在動」。 一個每天回報的 bot
曾被單趟擷取抓成 24 則、內容全是幾週前的設定過程,據此推論「它從沒回報過」——
重抓後是 151 則,日報一直都在它自己的直接對話串上。另外,跨 bot 的往來還有獨立的
exchange 串(側欄與訊息裡的 ), 那是另一個容器,不是日報的所在地;要讀跨 bot 對話才需要展開它。
button[aria-label^="Open exchange with"]
- 元素引用会失效:一旦React重新渲染,会失败并报错
agent-browser click @e5,因为Could not locate element with role=button name=…指的是“特定快照中的第N个节点”。交互前请立即重新生成快照,或者完全绕过引用,使用@eN和DOM属性选择器——上面的示例用的就是后者。eval - 没有本地存储可以走捷径:、
localStorage和sessionStorage都是空的。DOM是唯一的数据源,因此没有“直接查数据库”的路径;完整导出每个机器人的数据需要一次点击和一次等待。indexedDB.databases() - 两个消息计数不一致:有一个对话显示标题为“3 messages with 2 Bots”,但DOM中有7个节点。哪个是权威数据,以及为什么会不一致,目前为
[role=group]状态——标题可能统计的是线程而非消息,或者DOM中包含渲染的系统条目。两个数字本身都不能作为可靠答案:如果对话看起来很短或者计数不一致,请滚动UNCONFIRMED容器,重新读取,并报告你实际看到的内容。log "Conversation transcript" - 计数不准:有一次
article只返回了一个eval,但快照显示有几十个。导出完整对话记录时,优先使用联合选择器article。'[role=article],article,[role=group]' - 一次抓不全,而且滚到顶部会删除数据。对话串是虚拟化列表:一跳到顶,底部节点就会被回收,等于边读边删(一次抓取因此丢失了一整天的最新消息)。可靠的做法是用两种独立方法各跑一遍再取并集:锚点法(抓取最顶部的那则
scrollTop = 0,调用[role=group],直到顶端消息连续数次不变且总数不再增长——两个条件要同时满足,只看一个会把“滚不动”误判成“到顶了”)与像素法(scrollIntoView({block:"start"})的[role=log][aria-label="Conversation transcript"]从顶部往下每次滚动0.8屏)。去重键使用scrollTop,不要用DOM节点引用——同一则消息被回收重建后是不同节点,内容才是稳定的身份标识。两种方法的数字一致才算抓全:五个机器人两种方法各自给出151/151、53/53、38/38、10/10、7/7,这是可信的完整性证据;另一个机器人两种方法给出277 vs 132、并集372,就只能标记为aria-label + 前120字。UNCONFIRMED - 对话串短,先怀疑抓取方法,不要当成“这个机器人没在运行”。有一个每天上报的机器人曾被单趟抓取抓到24则消息,内容全是几周前的设置过程,据此推论“它从没上报过”——重新抓取后有151则,日报一直都在它自己的直接对话串上。另外,跨机器人的往来还有独立的exchange串(侧边栏和消息里的),那是另一个容器,不是日报的所在地;只有需要读取跨机器人对话时才需要展开它。
button[aria-label^="Open exchange with"]
Where this came from
内容来源
Everything above was executed against version on macOS. The app ships
an embedded panel (a bot can hand its screen over for
interactive login and take it back) which is visible in the tree but unexplored
— if a task needs it, expect to map it yourself and write down what you find.
0.39.0Grok Bot's Computer以上所有内容均在macOS上针对版本测试验证。该应用自带一个嵌入式的“Grok Bot's Computer”面板(机器人可以交出其屏幕用于交互式登录,之后再收回),该面板在DOM树中可见但尚未探索——如果任务需要用到它,你需要自行梳理其结构并记录发现。
0.39.0