ClawdHome 需要为每个受管 macOS 用户提供一套独立、可复用的网页登录环境,用来承载 OpenClaw、Hermes、OpenCLI Browser Bridge、OAuth 登录和后续网页自动化能力。
Browser Account 的核心目标:
- 每个受管用户拥有独立 Chrome profile,cookie/session 不互通。
- 不污染当前管理员或用户自己的主 Chrome/Arc/Safari。
- 不要求受管用户登录 macOS GUI 桌面。
- OpenClaw、Hermes 和未来 runtime 共用同一套用户级浏览器能力。
- 命令行中的常见浏览器打开动作自动进入
clawdhome-browser open <url>。 - OAuth 授权链接和 Browser Bridge 启动前置都走该用户的 ClawdHome Chrome。
重要边界:Chrome GUI 进程实际由当前 macOS console 用户启动,因为 macOS 图形应用必须挂在当前桌面会话上;受管用户只拥有命令入口、session 文件、工具文件和状态文件。隔离依赖独立 --user-data-dir,不是“Chrome 进程以受管用户身份运行”。
- 用户级,不绑定虾/OpenClaw/Hermes 路径:新路径统一放在
~/.clawdhome、~/.local/bin和~/.npm-global/bin。 - Runtime 消费,不拥有能力:OpenClaw 和 Hermes 只是调用 browser account,不分别实现浏览器。
- GUI 启动集中化:所有需要打开浏览器的路径最终落到
BrowserAccountManager或clawdhome-browser。 - CDP 只本机可见:Chrome 使用
--remote-debugging-address=127.0.0.1。 - 私有状态文件:session 文件为目标用户所有,权限
0600。 - 兼容旧路径:
.openclaw仅用于旧 session/profile 读取和 reset 清理,不再作为新安装路径。
Browser Account 不是一个 UI 小功能,而是 ClawdHome 的第二层隔离边界。
宿主操作者账号 / 默认浏览器
└── 不直接暴露给 runtime
Shrimp macOS 用户边界(第一层隔离)
├── ~/.openclaw / ~/.hermes / ~/.clawdhome
└── Browser Account 命名空间(第二层隔离)
├── managed Chrome profile
├── Browser Bridge / OpenCLI profile selector
└── CDP session metadata
runtime 消费层
├── OpenClaw
└── Hermes
解释:
- 第一层隔离是 macOS 用户边界,负责隔离文件、进程、Keychain、配置目录。
- 第二层隔离是 Browser Account 边界,负责隔离网页登录态、Browser Bridge profile、CDP endpoint 和浏览器工具入口。
- OpenClaw 与 Hermes 对同一只 Shrimp 会共享第二层隔离,这样引擎切换不需要重复登录。
- “支持外部浏览器”在 ClawdHome 语境里,指 runtime 使用 Shrimp 托管浏览器账号,而不是直接使用操作者自己的默认 Chrome profile。
| 资源 | 同一 Shrimp 内 OpenClaw / Hermes | 不同 Shrimp 之间 | 与宿主操作者默认浏览器 |
|---|---|---|---|
~/.clawdhome/browser/session.json |
共享 | 隔离 | 隔离 |
~/.clawdhome/browser/opencli-profile.json |
共享 | 隔离 | 隔离 |
| 托管 Chrome profile | 共享 | 隔离 | 隔离 |
| Browser Bridge 扩展连接态 | 共享 | 隔离 | 隔离 |
runtime 配置目录(.openclaw / .hermes) |
各自独立 | 隔离 | 隔离 |
| 路径 | 归属 | 权限 | 用途 |
|---|---|---|---|
/Users/<console>/Library/Application Support/ClawdHome/BrowserProfiles/<username>/ |
当前 console 用户 | Chrome 默认 | ClawdHome UI/launcher 启动的 Chrome profile |
/Users/<console>/Library/Application Support/ClawdHome/BrowserProfiles/<username>/ClawdHomeExtensions/opencli-browser-bridge/ |
当前 console 用户 | 0755 |
OpenCLI Browser Bridge unpacked Chrome extension |
/Users/<username>/.clawdhome/browser/ |
目标用户 | 0755 |
browser session、debug、warmup 状态目录 |
/Users/<username>/.clawdhome/browser/session.json |
目标用户 | 0600 |
当前 CDP endpoint、profile、端口、console 用户 |
/Users/<username>/.clawdhome/browser/install-warmup.json |
目标用户 | 0600 |
初始化预热完成标记 |
/Users/<username>/.clawdhome/browser/debug.log |
目标用户 | 用户可写 | wrapper / launcher / Python CLI 调试日志 |
/Users/<username>/.clawdhome/browser/profile/ |
目标用户 | Chrome 默认 | 无 ClawdHome session 时用户命令兼容 profile |
/Users/<username>/.clawdhome/tools/clawdhome-browser/clawdhome-browser |
目标用户 | 0755 |
Python CLI 主体 |
/Users/<username>/.local/bin/clawdhome-browser |
目标用户 | 0755 |
runtime 无关入口,Hermes 优先走这里 |
/Users/<username>/.npm-global/bin/clawdhome-browser |
目标用户 | 0755 |
OpenClaw/npm 环境入口 |
/Users/<username>/.local/bin/open 等 |
目标用户 | 0755 |
常见浏览器命令 wrapper |
/Users/<username>/.npm-global/bin/open 等 |
目标用户 | 0755 |
OpenClaw 维护命令中的浏览器命令 wrapper |
/Users/<username>/.local/bin/open-cli |
目标用户 | 0755 |
npm open-cli URL opener wrapper |
/Users/<username>/.npm-global/bin/open-cli |
目标用户 | 0755 |
npm 全局环境中的 open-cli wrapper |
/Library/Application Support/ClawdHome/BrowserLaunchers/<username>/clawdhome-browser-launcher |
root:wheel |
4755 |
从目标用户命令借助 console GUI session 拉起 Chrome |
/Users/<username>/.clawdhome/TOOLS.md |
目标用户 | 用户可读 | 工具说明,给 agent/runtime 使用 |
| 层 | 文件 | 职责 |
|---|---|---|
| UI - OpenClaw 用户详情 | ClawdHome/Views/UserDetailView.swift |
概览页浏览器账号卡片:打开、安装工具、刷新、重置 |
| UI - Hermes 详情 | ClawdHome/Views/HermesDetailView.swift |
左侧“浏览器”入口和 Hermes 浏览器配置页 |
| 维护窗口 | ClawdHome/ClawdHomeApp.swift |
授权链接按钮通过用户浏览器账号打开 |
| 终端链接处理 | ClawdHome/Views/TerminalLogView.swift、HermesDetailView.swift |
用户点击 http(s) 链接时调用 openBrowserAccountURL |
| XPC 协议 | Shared/HelperProtocol.swift |
Browser Account 方法声明 |
| XPC 客户端 | ClawdHome/Services/HelperClient.swift |
App 侧 async 封装 |
| XPC Helper surface | ClawdHomeHelper/HelperImpl+BrowserAccount.swift |
XPC 到 manager 的薄转发 |
| Manager | ClawdHomeHelper/Operations/BrowserAccountManager.swift |
Chrome 启动、session 写入、工具安装、wrapper 生成、reset |
| Runtime env - OpenClaw | ClawdHomeHelper/Operations/UserEnvContract.swift |
OpenClaw PATH 中优先放入 browser wrapper |
| Runtime env - Hermes | ClawdHomeHelper/Operations/HermesInstaller.swift、HermesGatewayManager.swift |
注入 BROWSER=clawdhome-browser open %s |
| Models | Shared/BrowserAccountModels.swift |
路径常量、session/status、DevToolsActivePort 解析 |
当前代码里的运行时接入关系如下:
| Runtime | 共享 Browser Account | BROWSER wrapper |
OPENCLI_PROFILE |
BROWSER_CDP_URL |
额外配置同步 |
|---|---|---|---|---|---|
| OpenClaw | 是 | 通过 PATH wrapper 间接消费 | 是 | 是 | 无 |
| Hermes | 是 | 直接注入 | 是 | 可达时注入 | 同步到 browser.cdp_url |
补充说明:
- OpenClaw 当前更偏向消费 Browser Bridge /
opencliwrapper /openwrapper 这条链路。 - Hermes 同时消费 Browser Bridge 和 CDP 两条链路,因此在启动环境和
config.yaml中都保留了 CDP endpoint。 - 这份矩阵描述的是“当前实现状态”,不是未来唯一方案;如果后续要给 OpenClaw 增加显式 CDP 注入,应在这里更新矩阵。
BrowserAccountSession 写入 ~/.clawdhome/browser/session.json,主要字段:
username:目标受管用户。profilePath:实际 Chrome profile 路径。devToolsActivePortPath:Chrome 写出的DevToolsActivePort文件。httpEndpoint:本机 CDP HTTP endpoint,例如http://127.0.0.1:35750。webSocketDebuggerURL:CDP WebSocket URL。cdpPort:CDP 端口。launchedAt:启动时间。consoleUsername:实际承载 GUI Chrome 的 console 用户。
BrowserAccountStatus 用于 UI 展示:
toolInstalledsessionExistsbrowserReachablehttpEndpointprofilePathtoolPathmessage
sequenceDiagram
participant UI as "ClawdHome UI"
participant Helper as "Privileged Helper"
participant Manager as "BrowserAccountManager"
participant Chrome as "Google Chrome"
UI->>Helper: openBrowserAccount(username)
Helper->>Manager: open(username)
Manager->>Manager: resolveContext(username)
Manager->>Manager: resolve current console user
Manager->>Manager: create BrowserProfiles/<username>
Manager->>Manager: download/unzip OpenCLI Browser Bridge extension
Manager->>Chrome: launchctl asuser <consoleUID> open -na Google Chrome --args --user-data-dir=<profile> --remote-debugging-port=0 --load-extension=<profile>/ClawdHomeExtensions/opencli-browser-bridge
Chrome-->>Manager: DevToolsActivePort
Manager->>Manager: write ~/.clawdhome/browser/session.json
Manager-->>UI: BrowserAccountSession JSON
说明:
username是受管 macOS 用户,不是 runtime 类型。- Chrome GUI 进程属于 console 用户;登录态隔离依赖独立 profile。
- OpenCLI Browser Bridge 扩展随 profile 安装并通过
--load-extension加载,不要求用户手动进入chrome://extensions。 - 找不到系统 Google Chrome 时返回安装提示,不自动下载 Chrome。
- session 文件写入前会确保
~/.clawdhome/browser归目标用户所有,避免 root 创建目录后目标用户无法写日志。
flowchart TD
A["installBrowserAccountTool(username)"] --> B["BrowserAccountManager.installTool"]
B --> C["写 ~/.clawdhome/tools/clawdhome-browser/clawdhome-browser"]
B --> D["编译 root:wheel 4755 launcher"]
B --> E["写 ~/.local/bin/clawdhome-browser"]
B --> F["写 ~/.npm-global/bin/clawdhome-browser"]
B --> G["写 open / chrome / chromium / xdg-open wrappers"]
B --> H["如存在 opencli, 保存真实入口并安装 opencli wrapper"]
B --> I["写 open-cli URL opener wrapper"]
B --> J["追加 ~/.clawdhome/TOOLS.md"]
安装位置分两类:
~/.local/bin:runtime 无关入口,Hermes、Python、shell 工具优先使用。~/.npm-global/bin:OpenClaw/npm 生态入口,覆盖 OpenCLI 这类 npm 全局命令。
wrapper 写日志时使用 || true,日志目录权限异常不会导致 open、opencli 或 open-cli 命令直接失败。
官方手动流程是下载 OpenCLI release 中的 extension zip,然后在 chrome://extensions 中开启 Developer Mode 并 Load unpacked。ClawdHome 在浏览器初始化时自动完成等价流程:
- 请求
https://api.github.com/repos/jackwener/opencli/releases/latest。 - 选择 release assets 中名称包含
extension且后缀为.zip的包,例如opencli-extension-v1.0.4.zip。 - 下载并解压到当前 Chrome profile:
<profile>/ClawdHomeExtensions/opencli-browser-bridge/。 - 验证解压结果必须包含
manifest.json,并拒绝 zip 中的绝对路径或..路径。 - 按用户名计算该用户独立的 OpenCLI daemon 端口,并把扩展
dist/background.js中的DAEMON_PORT改写为该端口。 - 启动 Chrome 后通过 CDP
Extensions.loadUnpacked加载该扩展。
扩展目录放在 profile 下,而不是目标用户 home 下。原因是 macOS GUI Chrome 进程实际由 console 用户承载,profile 及 unpacked extension 必须对该 GUI 进程可读。
所有 ClawdHome 管理的 Chrome 启动路径都应通过 clawdhome-browser-pipe-launcher 加载 OpenCLI Browser Bridge。clawdhome-browser 以目标用户身份运行,通常不能读取 console 用户 Library/Application Support 下的 profile,因此它只负责请求启动;扩展下载、端口改写、解压和 manifest.json 校验由 root helper / setuid launcher 侧完成。缺插件时 launcher 会失败并提示用户回到 ClawdHome 重新安装/初始化浏览器工具,不会裸启动没有 Browser Bridge 的 Chrome。
支持命令:
status:读取 session 并探测 CDP 是否可达。launch [url]:底层启动 Chrome。正常用户不需要直接使用。open <url>:启动/复用浏览器并打开 URL。title:读取当前页面标题。extract-text:提取当前页面正文。screenshot:保存当前页面截图。
open <url> 行为:
flowchart TD
A["clawdhome-browser open <url>"] --> B{"session 存在且 endpoint 可达?"}
B -- "是" --> C{"相同 URL tab 已存在?"}
C -- "是" --> D["activate tab"]
C -- "否" --> E["CDP /json/new?<url>"]
B -- "否" --> F["command_launch(url)"]
F --> G{"session 中有 profile/port?"}
G -- "是" --> H["setuid launcher 复用该 profile/port 拉起 Chrome"]
G -- "否" --> I["提示先在 ClawdHome 打开浏览器账号"]
注意:
- http(s) URL 会进入
clawdhome-browser open <url>。 - 无参数
open默认打开https://clawdhome.ai。 - 如果
https://clawdhome.ai已经打开,不会重复新建 tab,只激活已有页面。 CLAWDHOME_BROWSER_HIDE=1会在预热/Bridge 场景打开后尝试隐藏/关闭窗口,减少初始化打扰。
目标用户在维护终端或 runtime shell 中不能直接用 /usr/bin/open 启动 GUI Chrome,因为该 shell 不属于 console 图形会话。clawdhome-browser-launcher 解决的是“从目标用户命令进入 console GUI session 启动 Chrome”的问题。
链路:
- 目标用户执行
clawdhome-browser open <url>。 - Python CLI 读取
~/.clawdhome/browser/session.json。 - 如果需要重新启动 Chrome,查找
/Library/Application Support/ClawdHome/BrowserLaunchers/<username>/clawdhome-browser-launcher。 - launcher 以 setuid root 运行,但只读取调用者 home 下的 session。
- launcher 解析 session 中的 profile、port、console 用户。
- launcher 调用
launchctl asuser <consoleUID>,在当前 console GUI session 内启动 Google Chrome。
launcher 不接受任意 profile 参数,避免调用者把它变成任意 root GUI launcher。
安装后会接管:
openxdg-opensensible-browsergoogle-chromechromechromiumchromium-browser
wrapper 规则:
- 参数中包含
http://或https://:执行clawdhome-browser open <url>。 - 无参数:执行
clawdhome-browser open https://clawdhome.ai。 - 非 URL 参数:
open回退/usr/bin/open "$@";其他浏览器命令给出 ClawdHome 接管提示。
OpenClaw 维护终端还会通过 UserEnvContract.shellForcedExportPrefix 强制导出用户隔离 PATH,避免 macOS path_helper 把 /usr/bin/open 排到前面。
OpenCLI 的 Browser Bridge extension 必须在 Chrome 打开后才会连接。Browser Account 安装时如果发现 ~/.npm-global/bin/opencli,会:
- 保存真实入口为
opencli.clawdhome-real。 - 生成新的
opencliwrapper。 - 每次执行真实 OpenCLI 前,先运行:
CLAWDHOME_BROWSER_HIDE=1 clawdhome-browser open https://clawdhome.ai - Chrome profile 自动加载已安装的 OpenCLI Browser Bridge extension。
- wrapper 设置该用户专属
OPENCLI_DAEMON_PORT;如该端口上的 daemon 未运行,启动真实 daemon。 - 再执行
opencli.clawdhome-real <原参数>。
这样 opencli xiaohongshu search AI 不需要用户先手动打开浏览器。
同时会生成 open-cli wrapper。它只处理 http(s) URL 参数:
open-cli https://example.com会直接转为clawdhome-browser open https://example.com。- 非 URL 参数会返回 ClawdHome 接管提示,避免继续落到系统默认浏览器。
注意:如果模型显式执行 node /usr/local/lib/node_modules/open-cli/cli.js <url> 这种绝对路径调用,PATH wrapper 无法从系统层拦截。V1 不替换系统全局 /usr/local/lib/node_modules/open-cli/cli.js。
OpenClaw 相关入口:
- 用户详情概览页的“浏览器账号”卡片。
- 初始化 Wizard 的基础环境阶段。
installOpenclaw/reinstallOpenclawhelper 入口。- 维护窗口授权链接按钮。
- TerminalView 中点击 http(s) 链接。
初始化顺序:
- 修复 Homebrew 权限。
- 安装 Node.js。
- 配置 npm 目录。
- 设置 npm registry。
prepareBrowserAccountForRuntimeInstall(username)。- 安装 OpenClaw。
- 启动 Gateway。
prepareBrowserAccountForRuntimeInstall 会安装工具、首次打开 Chrome 写 session、关闭该 profile,并写入 install-warmup.json。同一用户再次初始化时不会重复打开 Chrome。
OpenClaw 当前使用 Browser Account 的关键方式:
- 通过用户隔离 PATH 命中
open/chrome/open-cli/opencliwrappers。 - 通过
OPENCLI_PROFILE选择 Shrimp 级 Browser Bridge profile。 - 在可达时通过
BROWSER_CDP_URL获取该 Shrimp 当前浏览器实例的 CDP endpoint。 - 复用 Browser Account 预热写出的
session.json,让后续clawdhome-browser open <url>能进入同一托管 profile。
这意味着 OpenClaw 已经可以使用“外部浏览器”,但这里的外部浏览器仍然是 ClawdHome 托管的 Shrimp 浏览器账号,不是宿主操作者的默认浏览器。
Hermes 相关入口:
- Hermes 左侧侧边栏“浏览器”。
- Hermes 浏览器配置页:状态、profile、工具路径、CDP、打开、安装工具、重置。
- Hermes 初始化 Wizard。
installHermeshelper 入口。- Hermes 维护终端和 Hermes chat/config terminal 的链接点击。
- Hermes gateway LaunchDaemon。
初始化顺序:
- 修复 Homebrew 权限。
- 安装 Node.js。
prepareBrowserAccountForRuntimeInstall(username)。- 安装 Hermes。
- 验证安装。
- 启动 Hermes gateway。
Hermes OAuth 关键点:
HermesInstaller.orderedRuntimeEnvironment注入:BROWSER=/Users/<username>/.clawdhome/tools/clawdhome-browser/clawdhome-browser open %sHermesInstaller.orderedRuntimeEnvironment在 session 可达时追加BROWSER_CDP_URL=<ws/http endpoint>,并在有 Browser Bridge profile 时追加OPENCLI_PROFILE=<profile>。HermesGatewayManager写入 LaunchDaemonEnvironmentVariables.BROWSER。HermesGatewayManager在 gateway plist 中按需写入BROWSER_CDP_URL和OPENCLI_PROFILE。- Python
webbrowser.open()和 Google OAuth 不再自行探测 Arc/Safari/Chrome,而是走 ClawdHome Browser。 HermesConfigWriter.syncBrowserCDPEndpoint会把当前 endpoint 同步到config.yaml的browser.cdp_url。- 诊断项会检查 Hermes gateway plist 中的
BROWSER是否符合预期。
Hermes 因为显式消费 CDP endpoint,所以在“外部浏览器”能力上比 OpenClaw 走得更深一层;但两者共享的仍然是同一份 Shrimp Browser Account 边界。
重置会备份/移除:
- UI profile:
/Users/<console>/Library/Application Support/ClawdHome/BrowserProfiles/<username>/ - 用户命令 profile:
/Users/<username>/.clawdhome/browser/profile/ - 旧兼容 profile:
/Users/<username>/.openclaw/browser-profile/ - 新 session:
/Users/<username>/.clawdhome/browser/session.json - 旧 session:
/Users/<username>/.openclaw/clawdhome-browser-session.json - warmup 标记:
/Users/<username>/.clawdhome/browser/install-warmup.json
profile 不直接删除,而是改名为 .backup-<yyyyMMdd-HHmmss>。
已经做的限制:
- CDP 只绑定
127.0.0.1。 - session 文件
0600。 - launcher 安装在 root-owned 目录,二进制
root:wheel 4755。 - launcher 只读取调用者 home 下的 session,不接受任意 profile/port 参数。
- 每个用户独立 Chrome profile。
.clawdhome/browser目录归目标用户,避免 root-created 目录导致 wrapper 日志写入失败。- runtime 看到的是 Shrimp 级托管浏览器,不是宿主操作者自己的默认浏览器。
非目标:
- 这不是强恶意多用户隔离。
- 本机 root、当前 console 用户、能读目标用户 session 的进程仍可干预浏览器。
- Chrome GUI 进程不是以目标受管用户身份运行。
新版本主路径为 .clawdhome,但保留旧路径迁移能力:
- 读取旧 session:
~/.openclaw/clawdhome-browser-session.json。 - 查找旧 launcher:
~/.openclaw/tools/clawdhome-browser/clawdhome-browser-launcher。 - reset 时备份旧 profile:
~/.openclaw/browser-profile。
这些路径只用于迁移和清理,不再作为新安装目标。
| 现象 | 常见原因 | 判断方式 |
|---|---|---|
open 打开 Arc/Safari |
PATH 没有命中 wrapper,或被 path_helper 重排 |
which open,检查是否为 ~/.local/bin/open 或 ~/.npm-global/bin/open |
Hermes Google OAuth 报 application "chrome" / -10810 |
Hermes 未继承 BROWSER,Python webbrowser 绕过 wrapper |
launchctl print system/ai.clawdhome.hermes.<user> 检查 BROWSER |
| OpenCLI 报 Browser Bridge 未连接 | Chrome 未预打开,extension 未连接 | 看 ~/.clawdhome/browser/debug.log 是否有 opencli wrapper 和 clawdhome.ai |
Hermes 生成 node /usr/local/lib/node_modules/open-cli/cli.js |
模型绕过 PATH wrapper,直接执行绝对路径 | PATH wrapper 无法拦截绝对路径;需要改调用方策略或全局 open-cli |
permission denied: ~/.clawdhome/browser/debug.log |
browser 目录归 root 或不可写 | ls -ld ~/.clawdhome/browser,应归目标用户 |
| 初始化卡在浏览器预热附近 | Chrome 打开后关闭失败或 launcher 无法进入 console session | 查 debug.log、`ps -axo pid,user,command |
clawdhome-browser status 不可达 |
session 旧端口失效或 Chrome 已关闭 | 运行 clawdhome-browser open https://clawdhome.ai 触发重启 |
- Debug build 成功。
- 安装 helper 并重启 ClawdHome。
- 两个受管用户分别打开浏览器账号,确认 profile 路径不同。
- 在两个 profile 内分别登录网站,确认 cookie 不互通。
- 检查工具文件:
~/.clawdhome/tools/clawdhome-browser/clawdhome-browser~/.local/bin/clawdhome-browser~/.npm-global/bin/clawdhome-browser~/.clawdhome/TOOLS.md
- 检查 wrapper:
which openwhich open-cliopen https://clawdhome.aichrome https://clawdhome.ai
- OpenClaw OAuth:维护窗口点击授权链接,不打开 Arc/Safari。
- Hermes OAuth:
webbrowser.open()/ Google OAuth 进入 ClawdHome Chrome。 - Hermes gateway:
launchctl print system/ai.clawdhome.hermes.<user>中存在BROWSER。 - OpenCLI:
opencli xiaohongshu search AI前置打开一次https://clawdhome.ai,Browser Bridge 能连接。 - Reset:目标用户登录态清空,其他用户不受影响。
- 安全回归:
- session 文件不是 world-readable。
- CDP endpoint 只在
127.0.0.1。 - launcher 为
root:wheel且 setuid。