- 人工智能
- AI Agent
- AI 应用
- 大模型
- 交互助手
- 工具调用
- Agent 编排
- 桌面应用
【免费下载链接】OpenMausBot
Open-source Grok Bot alternative with a virtual machine that bots can use
OpenMausBot 的频道与私聊支持@机器人提及:输入候选选择、多机器人配色高亮、Markdown 渲染和 RTL 多行方向对齐都围绕一套共享的提及识别算法实现。本文基于 提及高亮验证文档 展开,完整介绍如何启动隔离验证环境、逐项执行 QA 检查清单,并深入mentionRanges边界判定、MentionTextarea镜像布局与MAUS_COLORS色板注入等源码实现,帮助你在不接触任何真实账号的前提下复现整套验证流程。
一键启动隔离验证环境
整个验证的核心入口是一条命令:
node --experimental-strip-types scripts/verify-mentions.ts该脚本在本地拉起一个一次性的 fake-engine 服务器(不依赖任何真实账号、模型提供商或用户数据),并挂载真实的StoreProvider、Composer、ChatView与GroupView组件。从 scripts/verify-mentions.ts 的源码可以看到完整流程:
launchVerificationServer启动隔离服务器(来自 scripts/control-omb.ts 的 fixture 机制,fake CLI 为 server/testing/fake-claude-cli.ts);- 依次调用
runControlOmb(["new-bot", ...])创建Atlas、Juniper、調査担当三个机器人(含一个日语命名机器人); - 通过
/api/bots取出全部 bot id,用new-channel创建名为 "Design review" 的频道并拉入全部成员; mountPreview挂载预览入口entry: "/src/testing/mentions.tsx",路由__mentions.html;- 打印 JSON 形式的
previewUrl,随后parkUntilSignal()挂起等待;按Ctrl-C时finally块会关闭预览与 fixture 服务器,并删除临时数据目录(removeTempDir来自 server/testing/cleanup.ts)。
预览页面本身是一个极简测试壳:src/testing/mentions.tsx 提供 Channel / Direct chat / DM renderer preview 三个视图切换按钮和 Dark / Light 皮肤切换按钮(applySkin("midnight")/applySkin("atelier")),频道视图渲染<GroupView group={...} />,私聊视图渲染<ChatView bot={...} />——即真实生产组件,而非简化副本。
QA 验证清单(完整继承)
打开打印出的previewUrl后,按以下清单逐项手工验证。该清单直接继承自原文档,覆盖候选选择、键盘控制、多提及、Unicode 负例、长草稿对齐、私聊作用域、响应式与皮肤切换:
| 路径 | 操作 | 预期证据 |
|---|---|---|
| 频道输入框 | 输入@Jun,用 Tab 选中 Juniper,再追加普通文本,按 Enter 发送 | 已选中的名字保持高亮;频道以 Juniper 的 fake-engine 回复落定 |
| 候选键盘控制 | 输入@,用 ArrowDown 移动,用 Enter 选中日语名字;Escape 关闭候选列表 | 原生 textarea 保留已选名字与光标位置 |
| 多个提及 | 输入@Juniper Please review this. @Atlas Check the layout. | 草稿中两个完整名字均高亮 |
| 日语与 @everyone | 输入@調査担当 確認して,Shift+Enter 换行后再输入@everyone 確認して | 频道内两个名字均高亮,换行保留 |
| 负向匹配 | 输入me@Juniper.test @Ghost @Juniper2 @調査担当 確認して | 只有日语机器人被高亮 |
| 长草稿 | 粘贴 12 行文本,在聊天框中选择 Display,滚动 textarea 并在末尾编辑 | 镜像与 textarea 的宽度、折行高度、滚动偏移一致;12 个提及保留 |
| 直接对话 | 切换到 Direct chat;用鼠标选中 Juniper 并发送 | 用户气泡高亮提及,任务落定 |
| 私聊作用域 | 在 Atlas 的私聊中输入@everyone @Atlas @Juniper | 只有 Juniper 被高亮 |
| 响应式草稿 | 携带多提及草稿从桌面宽度缩到 390px | 输入框长到三行;镜像与 textarea 均测得 80px 高 |
| 皮肤切换 | Dark → Light → Dark | 输入框与已发送气泡中的名字均保持可读 |
| 机器人身份色 | 对比 Juniper(红)与 Atlas(蓝)及其头像 | 每个提及使用对应 bot 的 MAUS_COLORS 值;@everyone保持中性 |
| Markdown | 运行 ChatMarkdown 测试 | 散文/列表/表格中高亮已知名字;代码、链接与 HTML 安全性保持 |
其中"私聊作用域"一项对应源码中的everyone参数:@everyone是频道专属语义,mentionRanges(text, peers, everyone, person)只在传入everyone: true(即频道上下文)时才识别@everyone,私聊视图不传该参数,因此在 Atlas 的私聊里@everyone不产生高亮(见 src/lib/mentions.ts)。
核心实现一:边界与连续性判定
提及识别的关键难点是"何时算一个独立的@"。OpenMausBot 把这套规则放在 shared/mention-boundary.ts 中,注释明确说明其目标是"让渲染端与服务器路由端的提及识别保持完全一致":
// 只允许开启型标点/Markdown 标记作为 @ 前缀 const OPENING_MENTION_BOUNDARY = /[(*_~[{<'"\u2018\u201c\u3008\u300a\u300c\u300e\u3010\uff08]/u; export function isMentionBoundary(text: string, at: number): boolean { if (at === 0) return true; const before = text[at - 1]; return /\s/u.test(before) || OPENING_MENTION_BOUNDARY.test(before); } export function isMentionNameContinuation(value: string | undefined): boolean { return value !== undefined && /^[\p{L}\p{N}\p{M}_]/u.test(value); }两条规则解释了 QA 表中"负向匹配"一行的行为:
- 前缀规则:
me@Juniper.test中@前面是字母e(既非空白也非开启标点),isMentionBoundary返回 false,因此Juniper不高亮——/@bot依然被视为 URL 路径,而(@bot)、【@bot】、**@bot**才是标签; - 连续性规则:匹配到的名字后面若紧跟 Unicode 字母、数字、组合符或下划线,说明它只是更长单词的前缀,不成立。这就是 QA 表中
@Juniper2不高亮、而审查修复中@調査担当者、@everyone調査、补充平面字符与更长的同名前缀(roster 里存在精确更长的名字时)全部保持纯文本的原因。
@候选列表的过滤逻辑同样位于 src/lib/mentions.ts:mentionQueryAt从光标向前找最后一个@,要求前面是行首或空白,查询长度不超过 24 且不含@、换行;mentionChoicesForQuery做大小写不敏感的includes过滤,并在查询以空格结尾且恰好等于某个候选全名时清空列表(即输入完@Juniper后列表收起)。
核心实现二:mentionRanges 高亮算法
高亮的统一入口是mentionRanges(text, peers, everyone, person)(src/lib/mentions.ts),它返回一组{ start, end, color?, you?, person? }区间。算法要点:
- 按名字长度降序排序(
b.name.length - a.name.length),保证"先长后短"匹配,避免短名字抢先匹配长名字的前缀; - 逐字符扫描
@,先过isMentionBoundary边界检查,再做大小写不敏感的全名匹配,最后过isMentionNameContinuation连续性检查; - 颜色只接受色板值:源码注释
// Only palette values enter CSS明确——只有当peer.color是MAUS_COLORS的合法键时才写入区间,@everyone永远不带色,保持中性; - 除了机器人名字,还支持 person 标签(Team Cloud 队友)与 devs 标签两条旁路。
MAUS_COLORS色板定义在 src/lib/mascot.ts,共 12 色,与机器人头像共用同一套值——这正是 QA 表中"每个提及使用其 bot 的 MAUS_COLORS 值"这一检查项的落点:
export const MAUS_COLORS = { green: "#009957", blue: "#377FE6", red: "#D94B52", orange: "#E78531", purple: "#8057C8", cyan: "#0EA5C6", pink: "#D84F8B", yellow: "#D8A729", teal: "#01A492", coral: "#E5634E", silver: "#A8ADB4", chrome: "#B3BAC4", } satisfies Record<MausColor, string>;色板名来自 shared/wire.ts 的MAUS_COLOR_NAMES,因此色名是线上协议的一部分,渲染端与服务器共享同一类型约束。
高亮的视觉样式在 src/styles.css 中,核心是.mention-highlight:
.mention-highlight { --mention-color: var(--color-ink-secondary); color: color-mix(in srgb, var(--mention-color) 30%, var(--color-ink)); background: color-mix(in srgb, var(--mention-color) 24%, var(--color-inset)); border-radius: 3px; text-decoration: underline; text-decoration-color: var(--mention-color); box-decoration-break: clone; -webkit-box-decoration-break: clone; }机器人颜色通过 CSS 变量--mention-color注入(MentionText组件内联style: { "--mention-color": range.color }),前景用 30% 混入、背景用 24% 混入,从而在任何皮肤下保持可读。记录运行中用 CSS sRGB 混合比例核算了全部 70 种 bot 颜色/皮肤组合,最低文字对比度为 6.24:1;.mention-editor还有forced-colors: active下的回退:隐藏镜像、恢复原生文本色,保证 Windows 高对比度模式可用。
相关组件与测试:
- src/components/MentionText.tsx:把
mentionRanges区间切分成纯文本与<span class="mention-highlight">片段; - src/components/MentionText.test.ts 与 src/lib/mentions.test.ts:区间算法的单元测试;
- src/components/MentionColors.test.ts:多机器人配色隔离回归,混合全部十种色板颜色并交叉重复/反转的名字与花名册顺序,同时覆盖
MentionText与ChatMarkdown两条渲染路径(对应原文档"26 个聚焦测试通过"的回归描述); - src/lib/bot-identity.test.ts:验证每种颜色在
styles.css中都有对应的--identity-*皮肤 token 与.bot-identity[data-bot-color]声明。
核心实现三:MentionTextarea 镜像布局
QA 表中"长草稿"与"响应式草稿"两项检查的难点在于:原生<textarea>无法给部分文本上色,而加一个可见的高亮层又必须与文本逐像素对齐。src/components/MentionTextarea.tsx 的解法是透明 textarea + 可见镜像:
<div ref={mirrorRef} dir={props.dir} aria-hidden="true" className="mention-editor-mirror pointer-events-none absolute inset-0 overflow-hidden"> <MentionText text={String(props.value ?? "")} peers={peers} everyone={everyone} />{"\n"} </div> <textarea {...props} ref={inputRef} onScroll={(event) => { sync(); props.onScroll?.(event); }} />.mention-editor textarea设color: transparent(光标用caret-color: var(--color-ink)保持可见),实际看到的文字全部来自下面那层.mention-editor-mirror,由MentionText渲染带高亮的文本;sync()把 textarea 的 12 项文本布局属性(font-family、letter-spacing、line-height、padding-*等,见 MentionTextarea.tsx 的TEXT_LAYOUT)逐条复制到镜像,并同步clientWidth与scrollLeft/scrollTop——这就是"镜像与 textarea 宽度、折行高度、滚动偏移一致"这一检查项的实现基础;- 高度自适应:
input.style.height = min(scrollHeight, lineHeight * 6),即最多 6 行后开始滚动,并在改高度后恢复scrollTop防止跳动; - 窗口尺寸监听刻意只观察宽度(
if (nextWidth !== width) resize(); else sync();),注释说明是为了避免"测量出的高度又触发 resize 形成循环"; - 手机触摸设备(
@media (pointer: coarse))强制 16px 字号,防止 Safari 在聚焦时缩放导致键盘收起后标题栏被状态栏遮挡。
样式注释还点明了纪律:.mention-highlight不能改变 padding、border 或字重等会移动光标的指标("No padding/border/font-weight changes that shift the caret")。
Markdown 渲染路径:remarkMentions 插件
已发送消息走 Markdown 管线。src/lib/mentions.ts 的remarkMentions是一个 remark 插件,遍历语法树时跳过link、linkReference、code、inlineCode节点——这保证了 QA 表中"代码、链接与 HTML 安全性保持"的结论:链接文字与代码块里的@Juniper不会被高亮,也不会把任意 HTML 注入高亮 span。插件把命中的文本节点拆分为普通文本与{ type: "mention" }节点,hName: "span",类名由mentionClassName决定(机器人 →mention-highlight,本人 →mention-highlight mention-you,队友 →mention-highlight mention-person,后者使用虚线下划线区分)。src/components/ChatMarkdown.test.ts 覆盖散文、列表、表格场景下已知名字的高亮。
RTL 混写草稿的 bidi 对齐(2026-09-09 集成记录)
原文档记录了一次针对当前主干的 RTL 集成:六个原始提交 rebase 到上游922715f0(0.1.69),保留了新版翻译后的输入框标签、BotAvatar 渲染、Markdown 代码块控制、线程控制与逐块/逐行 bidi 行为。
修复前的现象是:阿拉伯语优先的草稿里,镜像是 LTR/isolate 方向、原生 textarea 却是 RTL/plaintext 方向,两者错位。修复手段只有两条,且都能在仓库中直接看到:
- 把
dir属性转发给镜像(见上文MentionTextarea中dir={props.dir}); - 镜像样式表声明
unicode-bidi: plaintext(src/styles.css 的.mention-editor-mirror,注释为"逐段独立匹配原生 textarea 的方向")。
效果是每段文字跟随原生输入的方向:阿拉伯语与希伯来语行右对齐,夹在中间的英语行左对齐。记录中的实测数据:三行草稿下两个节点均为 540px 宽、80px 高;把首行从阿拉伯语改成英语后,两个节点的计算方向同步切换。复现步骤(注意端口与 channel ID 是当次运行的打印值,新运行必须用自己的):
node --experimental-strip-types scripts/verify-mentions.ts --bot-mentions # 打开打印出的 previewUrl,在真实 composer 中填入三行: # مرحبا @Atlas راجع هذا # English @Juniper review this # שלום @調査担当 תודה # 截取 before/after 后按 Enter 发送。 node --experimental-strip-types scripts/control-omb.ts channels --url http://127.0.0.1:20657 node --experimental-strip-types scripts/control-omb.ts wait --channel 18b5bfe7-deb0-4962-b3b4-414e9ab90492 --timeout 60 --url http://127.0.0.1:20657 node --experimental-strip-types scripts/control-omb.ts messages --channel 18b5bfe7-deb0-4962-b3b4-414e9ab90492 --limit 20 --url http://127.0.0.1:20657证据文件:修复前、修复后、发送后(浅色)、DOM、wait、transcript。该次运行共 65 个聚焦测试(含方向转发、既有 bidi 控制、提及渲染、cloudflared 重试)、typecheck、生产渲染器构建与定向 oxlint 全部通过;两个 Windows symlink EPERM 测试在未修改的922715f0上复跑同样失败,与本次改动无关。
机器人自己发出的提及与 DM 预览
--bot-mentions标志是"机器人提及别人"场景的脚本化手段。从 scripts/verify-mentions.ts 可以看到其机制:
- 启用后脚本创建独立 scratch 目录,设置
FAKE_CLAUDE_REPLIES(一条包含 Juniper、調査担当、Atlas 提及的固定回复)与FAKE_CLAUDE_REPLY_STATE(共享计数器状态文件),launcher 会把FAKE_CLAUDE_*环境转发给 fake CLI; - 计数器保证只有第一条fake 回复携带提及,后续回复退化为默认文本,从而把频道内机器人之间的接力限制在有界范围内;
- 启动失败时 catch 块先清理 scratch 再抛出,
finally兜底关闭预览与服务器。
操作路径:在频道 composer 输入@Atlas Please ask the team to review.并发送。记录运行中,Atlas 的真实 bot 消息把 Juniper 渲染为红色、調査担当 为橙色、Atlas 为蓝色,重复提及在两种皮肤下都保留颜色,而@調査担当者等 Unicode 前缀保持纯文本、频道@everyone保持中性。
此外,预览页的DM renderer preview按钮以group.dm = true渲染同一份 fixture transcript,直接复用真实的GroupView/Composer组件而不创建 bot 到 bot 的服务器会话。该预览中 bot 回复与草稿里的@everyone均不高亮、输入@every也不出现候选,印证了"@everyone 仅限频道"的作用域规则(预览仅检查 DM 渲染,不实际发送消息)。该场景的证据:wait、transcript、DOM 配色、深色截图、浅色截图。
记录运行结果与测试矩阵
原文档保留了多轮完整记录,供追溯时参照:
0.1.61 首验(2026-09-07,Windows + Chromium,上游9c681f44):频道与私聊的wait结果均为settled,有界 transcript 存于 evidence/mentions/;长草稿检查测得 textarea 与镜像均为 584px 滚动高度、440px 滚动偏移,滚动中编辑不破坏对齐;390px 检查只覆盖 composer(既有频道头在该宽度下仍溢出);3,827 个 Vitest 测试通过、2 个失败(均为 WindowssymlinkSyncEPERM,在未修改基线上同样失败)。
bot 颜色后续(renderer-only):789 个渲染器测试、typecheck、构建、定向 lint 通过;共享头像色板同时提供每个提及的前景色调、背景色调与下划线颜色,颜色变化在纯文本与 Markdown 中同步体现;70 种组合最低对比度 6.24:1。
多机器人配色隔离:同一消息中 Atlas(蓝)、Juniper(红)、調査担当(橙)正序加反序重复,再跟中性的@everyone与无法识别的@Ghost;五种 DOM 快照各含 7 个提及且逐一匹配期望色板,浏览器无 console 错误。证据:深色、浅色、计算样式、wait、transcript。运行详情(隔离 APIhttp://127.0.0.1:21621,渲染器http://127.0.0.1:5173/__mentions.html):
node --experimental-strip-types scripts/verify-mentions.ts node --experimental-strip-types scripts/control-omb.ts channels --url http://127.0.0.1:21621 # 在真实频道 composer 填入三行并切到 Light,然后按 Enter。 # 发送后重填同一草稿用于截图对比。 # @Atlas first. @Juniper second. @調査担当 third. # @調査担当 reverse. @Juniper again. @Atlas last. # @everyone neutral. @Ghost plain. node --experimental-strip-types scripts/control-omb.ts wait --channel ec952c04-4e41-4a13-ba87-8c79b3137c05 --timeout 60 --url http://127.0.0.1:21621 node --experimental-strip-types scripts/control-omb.ts messages --channel ec952c04-4e41-4a13-ba87-8c79b3137c05 --limit 20 --url http://127.0.0.1:21621control-omb的完整 verb 面(channels、wait、messages、new-bot、send-channel等,输出均为 JSON,wait默认 30 秒)见 scripts/control-omb.ts 的 HELP 文本,其无头 CLI 设计另见 docs/headless-cli.md。
皮肤前后对比
| 皮肤 | 修改前 | 修改后 |
|---|---|---|
| Midnight | ||
| Atelier |
验证范围与边界
原文档明确了这套 fixture 的证明边界,复现时务必注意:
- 它验证的是浏览器渲染器行为与fake-engine 消息处理,不能建立打包版 Electron、Safari、操作系统 IME 候选窗、真实提供商委托行为的结论;
- 提及高亮使用当前花名册——历史消息中已不在花名册里的名字保持纯文本,不会高亮;
- 记录中的服务器日志落在
%TEMP%/openmausbot-verification-evidence/server-*.log(Windows 路径);每次新运行必须使用自己打印出的 URL 与返回的 channel ID,不可复用记录中的端口或 ID。
小结
OpenMausBot 的提及高亮由三层组成:shared/mention-boundary.ts提供渲染端与服务器共享的边界/连续性规则;mentionRanges+remarkMentions分别服务输入框镜像与已发送 Markdown 两条渲染路径,颜色只从 12 色MAUS_COLORS色板注入;MentionTextarea用透明 textarea 加同布局镜像解决原生输入框无法局部上色的问题,并用unicode-bidi: plaintext与dir转发保证混写草稿逐段对齐。scripts/verify-mentions.ts+scripts/control-omb.ts构成的隔离夹具让你可以在无账号、无真实数据的环境里完整复现上述全部行为。
- 人工智能
- AI Agent
- AI 应用
- 大模型
- 交互助手
- 工具调用
- Agent 编排
- 桌面应用
【免费下载链接】OpenMausBot
Open-source Grok Bot alternative with a virtual machine that bots can use
相关推荐
Olares 自定义域名设置指南:在 Olares Space 中完成 TXT/NS 验证与组织域名绑定
Olares 自定义域名设置指南:在 Olares Space 中完成 TXT/NS 验证与组织域名绑定 本篇围绕 Terminus(Olares)仓库中 自定
人工智能AI AgentAI 应用大模型交互助手工具调用Agent 编排桌面应用浏览器控制OpenMausBot 的 Bot Settings 隔离验证夹具:从手工验证路径到自动回归的完整实战
OpenMausBot 的 Bot Settings 隔离验证夹具:从手工验证路径到自动回归的完整实战 本篇指南围绕 OpenMausBot 仓库中的 Bot
人工智能AI AgentAI 应用大模型交互助手工具调用Agent 编排桌面应用浏览器控制浏览器与服务器基础:理解 Web 应用运行环境,打通 Vibe Coding 全栈开发第一关
浏览器与服务器基础:理解 Web 应用运行环境,打通 Vibe Coding 全栈开发第一关 导读 :本篇以 Datawhale vibe vibe 教程"1.
人工智能AI AgentAI 应用大模型交互助手工具调用Agent 编排桌面应用浏览器控制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考