mcpField
P
Personae

Personae

本地服务
github.com/leek-emperor浏览器自动化27 周前更新

关于 Personae

Personae 是面向多账号浏览的桌面浏览器,将每个浏览器身份隔离,并允许 Agent 控制已打开的窗口。它通过 MCP 按身份定位页面,避免依赖标题或网址区分账户。

主要功能

  1. 01隔离各身份的 Cookie 与登录状态
  2. 02为身份提供独立浏览器窗口
  3. 03通过身份名称定位页面目标
  4. 04控制页面导航、点击与输入
  5. 05读取页面文本并捕获屏幕截图
  6. 06支持操作身份所属的弹出窗口

适用场景

  1. 01同时登录同一网站的多个账户
  2. 02让 Agent 操作已打开的特定身份窗口
  3. 03处理身份窗口中的 OAuth 弹出页

MCP 相关

MCP 服务将浏览器身份列表及页面导航、交互、读取和截图功能提供给客户端。每项工具按身份限定目标窗口,也可指定该身份下的弹出窗口。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"leek-emperor-personae": {
"command": "npx",
"args": [
"-y",
"@leek-emperor/personae-mcp"
]
}
}
}
启动命令npx -y @leek-emperor/personae-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@leek-emperor/personae-mcp0.2.1

工具

13

收录的工具列表 · 更新于 2 周前

参数类型必填说明
sectionstring否可选:只关注某个方面时,填写关键词进行筛选,例如 snapshot / click / tab / eval / screenshot / batch。留空则返回全部语法。

详细介绍

Personae

Personae

多身份浏览器 · 每个身份都是彼此隔离的分区,每个窗口都可由 Agent 控制

内置 agent-browser 和 MCP 服务——无需安装 Node 或命令行工具

English · 中文

这是一款 Electron 桌面浏览器,结合了多账号隔离和让 AI Agent 操控浏览器两项功能。

  • 每个浏览器身份都是独立的 Chromium persist: 分区——Cookie、localStorage 和登录状态完全隔离,因此你可以同时使用多个账号登录同一网站。
  • 每个身份都有独立窗口,带有前进、后退按钮和地址栏,使用体验与普通浏览器无异。
  • 内置 agent-browser 二进制文件,并通过 MCP 暴露这些窗口,因此 Codex / Claude Code 可以操控特定身份对应的页面。

用户无需预先安装 Node、Chrome for Testing 或任何命令行工具。

MCP Registry

Personae 在首次支持 Registry 的版本发布后,已发布到官方 MCP Registry,名称为 io.github.leek-emperor/personae。 Registry 软件包是一个小型 stdio 启动器,适用于已单独安装并正在运行的 Personae 桌面应用——它不会取代浏览器应用,也不会创建云端浏览器。

安装并启动 Personae 后,支持 npm MCP 软件包的客户端可以运行:

npx -y @leek-emperor/personae-mcp

启动器通过本地桥接发现 Personae,并将每次工具调用限定在请求中指定的身份范围内。它不需要 API 密钥,也不会开放公网监听端口。建议使用应用中的 Configure Codex 控件进行配置,因为它使用内置的 Electron 运行时,无需安装 Node。

解决的问题

现有的浏览器自动化工具都假定由 Agent 启动浏览器。但如果你的产品本身_就是_浏览器客户端,情况则正好相反:窗口已经打开,并且分别属于不同的账号身份。Agent 需要做的是连接到相应窗口,同时避免跨越身份边界。

这里采用的方案:

  1. 应用启动时开放一个 CDP 端口(由系统分配,且仅限环回接口)。
  2. 身份窗口打开时,主进程通过 Target.getTargetInfo 获取其内容页面的权威 targetId。
  3. 本地桥接提供 identity → targetId 映射。
  4. MCP 服务直接将该 targetId 用作标签页引用,一步即可命中目标。

因此,即使两个窗口打开了同一网站,且标题和 URL 完全相同,snapshot(identity: "Account A") 和 snapshot(identity: "Account B") 仍会始终定位到正确的窗口。

快速开始

需要 Node ≥ 20 和 pnpm(仅开发时需要;打包后的应用不依赖它们)。

agent-browser 必须为 ≥ 0.36。低于此版本时,tab list --json 不会输出 targetId,而它是将身份与窗口关联起来的唯一信息——所有工具都会因此无法找到目标。package.json 中已锁定所需版本;请勿降级。

pnpm install
pnpm bundle:ab      # copy the agent-browser binary and core skill into resources/
pnpm dev

在界面中添加一个或两个浏览器身份,然后点击身份以打开其窗口。

界面默认使用英文。使用右上角的 EN / 中 切换按钮切换到中文;所选语言会被记住,也会应用到每个身份窗口内的工具栏,以及你复制的 Agent 提示词中。

测试

那些棘手且几乎没有文档说明的决策,封装在小型纯模块中,以便通过测试固定下来。它们使用 node:test 运行,不需要额外依赖:

pnpm test

测试覆盖:window.open / 弹出窗口决策(window-open.ts)、代理输入解析(proxy.ts)、地址栏 URL 规范化(url-input.ts)、Codex 配置 TOML 拼接(toml.ts)以及身份配色方案(colors.ts)。

打包:

pnpm build:mac      # or build:win

默认采用 临时签名(identity: '-'),无需 Apple Developer 证书,也能在构建机器上正常运行。但临时签名的应用无法分发——Gatekeeper 会在其他人的 Mac 上拦截它。若要正式分发,请通过环境变量提供证书,并将 notarize 设为 true:

CSC_LINK=/path/to/cert.p12 CSC_KEY_PASSWORD=... \
APPLE_ID=... APPLE_APP_SPECIFIC_PASSWORD=... APPLE_TEAM_ID=... \
pnpm build:mac

连接 Codex / Claude Code

最快的方法:让 Agent 自行连接

界面中有一个**“让 Agent 自行连接”**区块,会生成一段可直接发送的提示词。复制后粘贴到 Codex 或 Claude Code 中,Agent 就会编写自己的 MCP 配置、重启,并通过调用 list_identities 验证连接。

这段提示词不只是配置片段——它还会告诉 Agent 这 13 个工具的用途,以及需要避免哪些错误(过期的引用、猜测 agent-browser 语法、试图通过页面标题区分身份)。它根据实时运行时值生成,因此其中的路径始终适用于当前运行的机器。

或者点击一个按钮

同一面板还提供一键安装功能,会将 MCP 服务写入 ~/.codex/config.toml(支持幂等操作,并会先自动备份)。路径会在运行时根据 process.execPath 解析,因此不会出错。

或者手动配置

在 macOS 上:

[mcp_servers.personae]
command = "/Applications/Personae.app/Contents/MacOS/Personae"
args = ["/Applications/Personae.app/Contents/Resources/mcp-server.mjs"]

[mcp_servers.personae.env]
ELECTRON_RUN_AS_NODE = "1"

Contents/MacOS/ 下的可执行文件遵循 productName,因此名称为 Personae。请注意,electron-builder 的 executableName 仅适用于 Windows。

command 指向应用自身的二进制文件;ELECTRON_RUN_AS_NODE=1 会使其退化为纯 Node 运行时。因此无需安装 Node(已验证:将 PATH 设为 /usr/bin:/bin 后,完整流程仍可正常运行)。

Claude Code:

claude mcp add personae \
  --env ELECTRON_RUN_AS_NODE=1 \
  -- /Applications/Personae.app/Contents/MacOS/Personae \
     /Applications/Personae.app/Contents/Resources/mcp-server.mjs

MCP 工具

每个工具都需要一个 identity 参数(名称或 ID)。

工具用途
load_skill获取捆绑的 agent-browser 版本的实际命令语法(默认返回章节索引;传入 section 可获取特定章节)
list_identities列出所有 identity 的打开状态和当前 URL
open_identity打开 identity 的窗口
snapshot返回可访问性树快照,其中包含 [ref=eN] 元素引用
navigate导航到某个 URL
click / fill / press交互;click 接受引用或可见文本
act在一个 agent-browser 进程中运行多条命令
get_text / get_url读取内容
screenshot截取屏幕截图
eval_js执行 JavaScript

当 click / fill 接收到 @eN 引用时,会先自动获取快照,因为引用仅在单个 agent-browser 进程内有效——在多个进程间重复使用引用总会失败并提示 Unknown ref。进行多步交互时,请将操作放在同一个 act 批处理中。

操作弹出子窗口。 当页面打开弹出窗口(OAuth 登录、分享对话框等)时,该弹出窗口会成为同一 identity 所属的子窗口(参见弹出窗口和 OAuth)。它是单独的 CDP 目标,会在 list_identities 中作为 children 列在 identity 下。每个操作/读取工具都接受可选的 target 参数——传入子窗口的 targetId(或其索引)即可操作弹出窗口,而不是主窗口;省略该参数则仍像之前一样操作主窗口。

架构

┌─────────────────────────────────────────────────────────┐
│  Electron main process                                   │
│                                                          │
│  ├─ CDP server (port 0 → system-assigned, 127.0.0.1 only)│
│  ├─ IdentityManager   storage / window lifecycle / target │
│  └─ Agent Bridge      local HTTP, exposes identity↔target │
└───────────┬─────────────────────────────────┬────────────┘
            │                                 │
  ┌─────────▼──────────┐          ┌───────────▼───────────┐
  │ Identity window    │          │  Discovery file       │
  │ (one per identity) │          │  userData/            │
  │                    │          │  agent-bridge.json    │
  │ BrowserWindow shell│          │  (port changes every  │
  │  ├ WebContentsView │          │   launch; found via   │
  │  │   chrome.html   │          │   a fixed path)       │
  │  └ WebContentsView │          └───────────┬───────────┘
  │      content       │                      │
  │      ↑ agent acts  │                      │
  └────────────────────┘                      │
                                              │
            ┌─────────────────────────────────▼───────────┐
            │  scripts/mcp-server.mjs (stdio JSON-RPC)    │
            │  read discovery → get targetId → run CLI    │
            └───────────────────┬─────────────────────────┘
                                │
                    ┌───────────▼────────────┐
                    │  Codex / Claude Code   │
                    └────────────────────────┘
为什么每个 identity 窗口都是“一个外壳加两个 WebContentsViews”

目标是将第三方页面包裹在我们自己的导航 UI 中,而不使用 <webview> 标签。因此:

  • 外壳 BrowserWindow 仅用作容器,并加载 about:blank;
  • 顶部栏是本地的 chrome.html,带有专用预加载脚本,仅公开六种导航方法;
  • 内容区域是一个应用了分区的 WebContentsView,并且刻意不加载任何预加载脚本,以保持第三方页面原样。

嵌套的 BrowserWindow 和 BrowserView 也都可行,Agent 能操作这三者(均已验证)。选择 WebContentsView 是因为 BrowserView 在 Electron 的类型定义中标记为 @deprecated,也因为在 macOS 上,嵌套窗口是单独的原生窗口——拖动、调整大小和最小化都需要手动同步边界,始终无法真正呈现为“一个浏览器窗口”。

弹出窗口和 OAuth

页面可以通过 window.open(url, name, features) 打开弹出窗口,例如 OAuth 登录、“分享到…”对话框等。这些必须是真实窗口:OAuth 弹出窗口依赖 window.opener 和 postMessage/window.close() 将结果传回,因此不能将其简化为页面内导航,否则会破坏流程。

这里采用的方式是将弹出窗口视为其所属身份的一等子窗口,而不会尝试猜测“这是不是 OAuth 窗口”:

  • 同窗口还是子窗口。 普通的 <a target="_blank"> 或不带特性的 window.open 会保持为窗口内导航(没有 opener,也不会创建新目标)——这样是安全的,并且保持“一个身份,一个主目标”的约束。只有以 new-window 方式打开的窗口(即带窗口特性的 window.open)才会成为真实子窗口。
  • 属于该身份。 子窗口创建时会将 parent 设为该身份的外壳窗口,并使用相同的分区,因此会显示在正确的窗口上方,Cookie 也会存入正确的账户。window.opener 会保留,因此适用于各种 OAuth 方式。outlivesOpener: false 表示关闭身份时也会关闭其弹出窗口。
  • 弹出窗口拦截器(默认开启)。 唯一区分“应该打开”和“不应该打开”的条件是用户激活——过去一秒内是否发生过真实的点击或按键?这正是主流浏览器弹出窗口拦截器的工作方式:它绝不会检查域名或内容。没有前置输入、由脚本驱动的弹出窗口会被拦截,并以不打扰用户的提示消息告知;你可以在顶部栏关闭拦截器。
  • Agent 可以控制子窗口。 每个子窗口都是独立的 CDP 目标,会解析为其权威 targetId,并以 children 的形式列在身份之下。每个 MCP 工具都可以通过可选的 target 指定要操作的弹出窗口。这不会破坏面向外部的“每个身份一个主目标”约定,而是将其如实扩展为“一个主目标加已跟踪的子窗口”。

该判断逻辑(same-window / allow-child / ignore)位于纯函数 src/main/window-open.ts 中,并经过单元测试。

按身份设置代理

每个身份都可以通过自己的代理路由——适用于希望同一网站的账号来自不同地区,或测试地理位置行为的场景。代理由你自行提供(从 IPRoyal / Webshare / Bright Data 等服务商购买);Personae 仅提供设置并负责接入。

  • 适用范围。 代理通过 session.setProxy 设置在身份的 persist: 分区上,因此是真正按身份单独设置的——身份 A 可以通过美国 IP 出口,而身份 B 可以通过日本 IP 出口。不设置代理则直接连接。
  • 支持的类型。 HTTP、HTTPS 和 SOCKS5。你可以分别填写各字段,也可以将 socks5://user:pass@host:port 这样的完整字符串粘贴到主机输入框中。
  • 身份验证。 支持使用用户名和密码的代理。凭据绝不会写入代理规则字符串(Chromium 会拒绝其中的 user:pass@);身份验证通过 Electron 的 login 事件处理。
  • 密码在静态存储时会加密。 密码通过 safeStorage 存储(macOS 上使用 Keychain,Windows 上使用 DPAPI),保存在 userData/proxy-secrets.json 中,绝不会写入 identities.json,也绝不会发送回渲染进程。如果 safeStorage 不可用,则会回退到明文存储,界面也会说明这一点。
  • 测试连接。 点击按钮后,会通过该身份的会话获取 IP 回显服务,并显示实际的出口 IP,以便确认代理确实生效。
  • WebRTC 泄漏防护。 对任何使用代理的身份,都会应用 setWebRTCIPHandlingPolicy('disable_non_proxied_udp'),防止 WebRTC 绕过代理并泄露真实 IP。

这不是反检测浏览器。 它只会更改你的网络出口,不会做其他处理。除上述 WebRTC 防护外,它不会进行指纹伪装(Canvas / WebGL / UA / 时区 / 字体……),因此同一台机器上的多个身份仍会共用相同的浏览器指纹。如果平台根据指纹关联账号,单靠代理无法隐藏这一点。

代理解析(parseProxyInput / buildProxyRules)是 src/main/proxy.ts 中的纯函数,并经过单元测试,包括验证凭据不会泄露到规则字符串中。

遇到的问题

记录在这里,因为其中大多数问题没有任何文档说明。

CDP 端口按浏览器进程设置。 --remote-debugging-port 是进程级开关,没有对应的 webPreferences 设置,因此一个 Electron 应用恰好只有一个 CDP 服务端;无法为每个窗口分配独立端口。使用端口 0 让系统自动分配,然后从 userData/DevToolsActivePort 读取分配的端口——该文件会在成功绑定之前写入,因此还要探测 /json/version 以确认服务是否正常。

在 Electron 上,--pin-tab 会直接失败。 它会触发 Target.createTarget,而 Electron 会返回 Not supported。更糟的是,固定标签页的状态会持续保留:会话一旦使用过该功能,状态就会被持久化,即使执行 close --all 也无法清除,导致后续所有命令都无法正常工作。因此,每次调用都要显式传入 --no-pin-tab。

标题为空的页面目标会让 agent-browser 卡住。 如果 shell 窗口从未调用 loadURL,它在 CDP 中就会显示为 url 和标题均为空的页面目标;连接到该目标会一直卡住(tab list 不返回任何内容,甚至不会超时)。因此,shell 必须加载 about:blank 并设置一个易读的标题。

使用 about:blank,不要使用 data:text/html,<title>...——后者中的非 ASCII 标题会按 latin-1 解码,导致乱码。

设置标题也存在竞态:about:blank 加载速度很快,did-finish-load 可能会在监听器绑定之前触发;当多个身份同时打开时,标题就会停留在 about:blank。解决方法:同步调用一次 setTitle,加载后再调用一次。

绝不要通过标题、url 或标签页索引识别窗口。 多个身份打开同一网站时,标题和 URL 都相同;而且 CDP 目标的顺序与 agent-browser 的标签页索引顺序不匹配,因此也无法推算索引。只能使用 targetId。 **agent-browser 的技能内容会随版本变化——不要照抄网上的语法。**其自身的 SKILL.md 明确说明,其中不包含任何命令语法,必须通过 CLI 使用 skills get 获取。本项目就证明了这一点:网上找到的 tab --url "*settings*" 和 --pin-tab 在早期版本中并不存在,而 tab list --json 输出 targetId 是较新的功能——本项目的整套目标定位方案都基于它。

agent-browser 会从二进制文件所在位置向上搜索 skills/ 目录,这可能会与其他项目中同名的目录冲突。请显式传入 AGENT_BROWSER_SKILLS_DIR。

打包的二进制文件不能只依赖 asarUnpack。asarUnpack 会将文件放在 app.asar.unpacked/resources/bin/ 下,而 process.resourcesPath 指向 Contents/Resources——两者位置不同,因此打包后找不到二进制文件。使用 extraResources 使路径保持一致,并将 asarUnpack 限定为实际通过 ?asset 导入的文件,否则同一个二进制文件会被打包两次。

在 macOS 上,不能直接“跳过签名”。将 identity: null 设为 null 会让 electron-builder 完全跳过签名;应用包会保留 Electron 原有的 adhoc 签名,但资源已被修改,因此签名与内容不一致,应用会悄无声息地拒绝启动(没有错误,也没有日志;spctl 会报告 code has no resources but signature indicates they must be present)。正确做法是使用 identity: '-' 进行 adhoc 签名,并在 entitlements 中加入 com.apple.security.cs.disable-library-validation——entitlements 和 entitlementsInherit 都要设置,因为前者涵盖主进程。

**userData 目录取决于 package.json 的 name,而可执行文件取决于 productName。**这是两个不同的字段,而且 executableName 仅适用于 Windows——因此,如果它们不一致,你就会去错误的位置查找。本项目有意将两者都设为 Personae,以避免这种情况。(注意,在 Linux 上,文件系统区分大小写,因此以旧的小写名称创建的目录将无法被找到。)

**使用占位 URL 配置 publish: generic 会在打包的最后一步崩溃。**electron-builder 的模板将 provider: generic 和 url: https://example.com/auto-updates 配在一起。改用 provider: github 后,它会在打包结束时尝试推断发布渠道;由于缺少 owner/repo 上下文,会抛出 TypeError: Cannot read properties of null (reading 'channel')——构建其实已经完成,却仍以失败状态退出。本项目通过 CI 中的 gh release upload 发布,因此将 publish 设为 null 即可。

GitHub Actions 中作业级别的 if 无法访问 matrix 上下文。使用 if: inputs.platforms == matrix.name 来筛选平台会悄无声息地失败(actionlint 会提示 context "matrix" is not allowed here,但 GitHub 本身不会报错),因此无论输入是什么,都会构建所有平台。请在上游作业中生成矩阵 JSON,并通过 fromJSON 将其传给 strategy.matrix。

**使用 Electron 渲染 SVG 时,请抑制 window-all-closed。**图标脚本会循环执行“创建窗口 → 捕获 → 销毁 → 创建下一个窗口”,而每次 destroy() 都会将窗口计数降至零,默认情况下这会退出整个应用——表现为只处理完第一个尺寸就崩溃,子进程报告 No rendezvous client, terminating process (parent died?)。这看起来像是时序问题,但重试并无帮助。请注册一个空的 window-all-closed 处理程序。

另外还有两点:在 Retina 显示器上,capturePage 会按 devicePixelRatio 输出(请求 512 会得到 1024),因此要固定 zoomFactor / deviceScaleFactor;将中等长度的 SVG 内联到 data:text/html;base64,... 会超出 URL 长度限制,导致 loadURL 失败并返回 ERR_FAILED (-2),因此请使用临时文件并通过 file:// 引用。

已知限制和安全说明

  • 隔离是一种约定,而非架构边界。 CDP 端口没有访问控制。它只在回环地址上使用随机端口进行监听,但任何连接到该端口的本地进程都能操控所有身份,跨越分区边界。这是允许外部 Agent 接入所付出的直接代价。请勿在共享设备上处理敏感账户。
  • 普通链接会在当前窗口内导航。 setWindowOpenHandler 会将 window.open 和 target="_blank" 链接转换为当前窗口内导航,以确保一个身份始终对应一个主目标。真正的弹出窗口(例如使用带功能参数的 window.open 打开的 OAuth 窗口)是例外——它们会作为受跟踪的子窗口打开;请参阅弹出窗口和 OAuth。
  • 每个身份至少占用 3 个 CDP 页面目标(shell + 顶栏 + 内容),每个打开的弹出子窗口还会额外占用一个,因此规模大致会按身份数量的 3 倍增长。
  • 捆绑的 agent-browser 版本在构建时固定;上游修复不会自动纳入。
  • 运行时行为仅在 macOS(arm64)上经过验证,包括打包后的版本。Windows 打包可在 CI 中成功完成(macOS 和 Windows 均会构建并生成安装程序),但该应用从未在 Windows 上实际运行过,因此其运行时行为未经验证。Linux 不是构建目标。bundle:ab 仅为当前平台打包。
  • 临时签名的构建版本不可分发:它们只能在构建机器上运行,在其他设备上会被 Gatekeeper 拦截。正式分发需要你自己的证书并进行公证。
  • Codex 端尚未使用真实客户端验证:MCP 流程通过一个模拟客户端的脚本进行了测试(包括在无 Node 环境中测试打包后的版本),但从未使用实际的 codex 运行进行测试。
  • 代理支持只会更改网络出口,不会更改指纹。 请参阅按身份设置代理——它不是反检测浏览器。

项目结构

src/main/
  cdp.ts          CDP port setup/discovery, targetId resolution
  identity.ts     identity storage, window lifecycle, nav-bar IPC, proxy
  window-open.ts  pure decision for window.open / target=_blank (unit-tested)
  proxy.ts        proxy input parsing → proxyRules (unit-tested)
  secret-store.ts safeStorage-backed proxy password storage
  url-input.ts    address-bar input → URL normalization (unit-tested)
  toml.ts         TOML section splice for the Codex config (unit-tested)
  agent-bridge.ts local HTTP bridge + discovery file
  mcp-setup.ts    one-click Codex configuration
  index.ts        main entry and IPC registration
src/shared/
  colors.ts       identity palette — imported by BOTH main and renderer,
                  so a window's top-bar dot always matches its list entry
src/preload/
  index.ts        main-window API
  chrome.ts       top-bar preload (navigation methods only)
src/renderer/
  chrome.html     navigation-bar UI
  src/App.tsx     identity management and connection panel
  src/ProxyPanel.tsx     per-identity proxy settings UI
  src/agent-prompt.ts    builds the copy-and-paste prompt for agents
  src/assets/fonts/      self-hosted latin subsets (the CSP blocks
                         external font CDNs; CJK falls back to the system)
scripts/
  mcp-server.mjs           MCP server (stdio JSON-RPC)
  bundle-agent-browser.mjs bundles the binary and core skill
  make-icons.mjs           SVG → PNG / icns / ico
test/
  *.test.ts                node:test unit tests for the pure modules above
design/logo/
  icon.svg                 icon source (edit this, then run pnpm icons)
  concept*.svg             alternative concepts from the design pass
.github/workflows/
  release.yml     build and publish a GitHub Release (manual)
  check.yml       lint / typecheck / packaging smoke test (manual)

resources/bin/ 和 resources/skills/ 由 pnpm bundle:ab 生成,不纳入版本控制。

图标

源文件为 design/logo/icon.svg——三张互不重叠的彩色卡片代表三个隔离的身份,指针则代表 Agent 控制。编辑 SVG 后,请为所有平台格式重新生成图标:

pnpm icons

这会生成 build/icon.png(1024)、build/icon.icns、build/icon.ico 和 resources/icon.png(512,即运行时窗口图标)。

图像通过Electron 内置的 Chromium渲染,因此无需 rsvg-convert / Inkscape / ImageMagick——这些工具在干净环境中通常并不存在,而 Electron 本来就是本项目的依赖项。.icns 由系统的 iconutil 组装;.ico 则逐字节写入。

发布

这两个工作流都只能手动运行(workflow_dispatch),不会在推送或标记时运行。

在 GitHub 上进入 Actions,选择一个工作流,然后点击 Run workflow:

工作流用途输入
Build & Release构建 macOS + Windows,创建发布version(可选)、platform(all / macos / windows)、create release、prerelease
Checklint + 类型检查 + 打包冒烟测试是否运行打包

创建的发布为草稿;请检查各项资源,然后自行点击 Publish。如果标记已存在,资源会追加到该标记(--clobber 会覆盖同名文件),因此重新运行不会直接失败。

发布不会使用 electron-builder 自带的发布步骤(electron-builder.yml 中的 publish: null);而是由单个汇总作业使用 gh release upload 上传所有内容,因为如果三个平台并行运行,它们都会尝试创建同一个发布并互相覆盖。

当 Build & Release 工作流创建发布时,它还会将版本匹配的 @leek-emperor/personae-mcp stdio 启动器发布到 npm,然后使用官方 mcp-publisher CLI 发布 server.json。首次发布前,请设置仓库的 NPM_TOKEN 密钥(具有发布该公开软件包权限的 npm 自动化令牌);MCP Registry 身份验证使用 GitHub OIDC,无需单独的密钥。

CI 生成的 macOS 构件也会进行临时签名。如需正式签名,请将 CSC_LINK / CSC_KEY_PASSWORD 添加到仓库密钥,并在 electron-builder.yml 中将 notarize 设为 true。

许可证

MIT — 请参阅 LICENSE。

捆绑的 agent-browser 采用 Apache-2.0 许可证;版权归其作者所有。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。