集成和登录¶
本指南说明登录如何到达 OpenBot、已加入的服务器如何通过其用户的计算机进行登录,以及如何添加提供方、应用程序或连接器。每一节都包含带有真实文件路径的可用示例。
登录如何到达 OpenBot¶
运行 OpenBot 的计算机保留所有授权。在已加入的服务器上,这是宿主机,而不是执行登录的用户的计算机。登录使用以下类型之一:
| 类型 | 示例 | 在本计算机上 | 在已加入的服务器上 |
|---|---|---|---|
| 回环回调 | URL MCP 服务器(McpOAuth)、Codex 浏览器登录、代理的 CLI 打印的页面 |
页面将浏览器发送到 http://127.0.0.1:<port>/...。MCP 监听器使用端口 28417 和路径 /mcp-auth(mcp-oauth-redirect-server.ts)。 |
回调中继(sign-in-relay-v1)。较旧主机上的 MCP:宿主机实时视图(mcp-sign-in-v1)。 |
| 设备代码 | Codex(codex-device)、Grok、Cline、Muse、GitHub 连接器 |
用户在提供方页面上输入一个短代码。不会有任何内容返回给 OpenBot。 | 宿主机启动它,客户端显示代码(providers-v1 到 providers-v5)。Muse:中继打开其设备页面,没有回调。GitHub:connectors-v1。 |
| 粘贴的代码 | Linux 上的 Claude(cli / paste)、Cursor(cli / link) |
页面显示一个代码,用户将其粘贴到 OpenBot 中。 | 宿主机启动它,客户端获取代码(providers-v3 到 providers-v5)。 |
| CLI 自有的浏览器登录 | Claude(auth login)、Codex、Gemini(acp-authenticate) |
提供方的 CLI 打开浏览器并接收其自己的回调。 | 中继(provider/start):宿主机捕获页面,客户端将回调查询发送到宿主机上 CLI 的监听器。Grok、Cursor 和 Cline:属于代码类型。 |
| 深度链接 | Slack、Discord(openbot://slack-workspace、openbot://discord-guild)、MCP 回退(openbot://mcp-auth) |
托管页面以 openbot:// 链接结尾,该链接只能到达打开它的计算机。 |
Slack 和 Discord:connectors-v1。客户端将密封的授权转发给启动安装的宿主机。浏览器客户端从自身来源的返回页面获取授权(web-messaging-install.ts)。 |
| 终端 | Pi | OpenBot 在宿主机上的终端中打开 CLI(provider-terminal-login.ts)。 |
在宿主机上登录。 |
| 密钥或本地会话 | API 密钥、1Password、Bitwarden | 用户输入密钥或提供 CLI 会话。 | 密钥通过加密通道发送到宿主机。Vault 审批保留在宿主机上。Bitwarden 会话密钥仅在其创建的计算机上有效;已加入的服务器获取 API 密钥和主密码。 |
提供方的登录类型位于 provider-drivers.ts 中的 ProviderSignIn 和 ProviderCodeSignIn。应用程序的类型是目录条目的 auth 类型:link、key 和 local。
MCP 登录如何续期和结束¶
McpOAuth(mcp-oauth-provider.ts)在线程启动或测试之前续期令牌,无需浏览器。续期调用 SDK 的 refreshAuthorization,而不是 auth(),因此它永远不会注册新客户端。令牌端点的响应决定保留的内容:
| 响应 | 令牌 | 客户端注册 | 登录应用、发现 | 结果 |
|---|---|---|---|---|
| 新的令牌集 | 已替换 | 已保留 | 已保留 | 已登录 |
invalid_grant |
已丢弃 | 已保留 | 已保留 | 需要登录(lostAt) |
invalid_client、unauthorized_client |
已丢弃 | 已丢弃 | 已保留 | 需要登录(lostAt) |
| 无网络、服务器错误、超时 | 已保留 | 已保留 | 已保留 | 无变化 |
被丢弃的令牌不会交给代理:accessToken 返回 null。IPC McpSignInState.needsSignIn 指示面板显示登录徽章和登录按钮,并且不附带测试。冻结的 mcp-sign-in-v1 协议仅保留 mcpServerId 和 signedIn。
注销和移除会先删除本地令牌。然后 OpenBot 对每个令牌发送一次尽力而为的 RFC 7009 请求,优先发送刷新令牌,发送到授权服务器的 revocation_endpoint。该请求通过 secureOAuthFetch 发送,携带注册的客户端身份验证,并设置 5 秒限制。失败仅记录来源和状态,并且不会显示。
secureOAuthFetch 限制每个 OAuth 请求:请求体最多 1 MiB,并且响应必须在 20 秒内到达。外部截止时间保持不变。对 MCP URL 本身的请求属于 MCP 流量,因为探测使用相同的 fetch 作为传输,所以没有大小或时间限制。当 MCP URL 的主机仅解析到公共地址时,每个 OAuth 主机(metadata、authorization server、token、registration 和 revocation 端点)也必须仅解析到公共地址,否则请求会在建立连接之前以 error.backend.mcpOAuthPrivateEndpoint 失败。查找失败也会停止请求。局域网或 localhost 的 MCP URL 没有此类检查。该检查不会固定地址,因此 DNS 可能在检查与连接之间发生变化;这种残余风险可接受。运行 bun run test:mcp-oauth 以针对本地授权服务器检查登录路径。
代理如何访问已登录的 MCP 服务器¶
Agent 永远不会获得 OAuth token。McpGateway.endpoint 会为每个 http 行提供一条指向 McpOAuthProxy(mcp-oauth-proxy.ts)的路由,只要它在这台计算机上有登录态,或有服务器已结束的登录态,并且没有用户输入的 Authorization 头。GitHub 行保留自己的代理。其他行、stdio 行和 Test 探针保持不变。
| 部分 | 规则 |
|---|---|
| 地址 | http://127.0.0.1:<port>/mcp/<route>。路由是行 ID 及其 URL 的摘要,因此 URL 改变就会产生新路由。 |
| Bearer | 路由与代理密钥的 HMAC。一个 bearer 只能打开一条路由。它使用 timingSafeEqual 进行比较,并注册为需要脱敏的内容。 |
| 以 403 拒绝 | Host 不是 127.0.0.1:<port> 或 localhost:<port>、未知路径、错误的 bearer。 |
| 上游 | 该行的 URL 及其请求头,并附上当前 token。不会发送 provider 的 Authorization 和 cookies。只跟随同源的 307 和 308 重定向,最多 3 次。 |
| 服务器返回 401 | 执行一次 McpOAuth.refreshAccessToken,然后用新 token 重试一次。 |
| 无登录态 | 每个请求都会收到 JSON-RPC 错误 -32001,通知收到 202,GET 和 DELETE 收到 405。代理永远不会返回 401,因此 provider 不会启动自己的登录。 |
| 服务器不可达 | 返回 502,并附带 JSON-RPC 错误 -32603。 |
每个请求都会从已启用的行和已存储的登录态中读取其目标。移除、关闭、编辑或注销某一行会立即使其路由失效;不存在需要保持最新的路由列表。无法启动的代理会以 error.backend.mcpOAuthProxyUnavailable 将该行从交接中移除。原始 token 永远不会作为回退。
端口和密钥以加密方式保存在 openbot-mcp-oauth-proxy-v1.json 中。Codex 会保留恢复线程的 MCP 配置,因此重启后同一地址必须仍然有效。当保存的端口已被占用时,代理会使用新端口和新密钥,因此旧的 bearer 会失效。Codex 工具清单会记录代理地址和被代理的行;如果有变化,会在下一次轮次时替换 Codex 会话。没有代理行的计算机保留其清单。
运行 bun run test:mcp-oauth 可对照本地授权服务器和本地 MCP 服务器检查这些路径。
远程登录:回调中继¶
用户连接到一个已加入的服务器,例如云服务器。主机启动登录,但登录页面会在用户自己电脑的浏览器中打开。用户的浏览器保留着他们的密码、通行密钥(passkeys)和会话。用户登录后,页面会将浏览器重定向回一个回环地址。用户的 OpenBot 接收该回调并将其发送给主机。主机完成登录并保留授权。
可选的 Team API 能力 sign-in-relay-v1 承载该中继。冻结的契约是 sign-in-relay-v1.ts。它有三个启动路由,provider-account-relay-v1 增加了第四个。它们共享 status、complete 和 cancel:
| 启动路由 | 登录什么 | 主机如何传递回调 |
|---|---|---|
mcp/start |
一个基于 URL 的 MCP 服务器,来自服务器设置或 Marketplace | 它将代码交给 McpOAuth(自有流程)。 |
provider/start |
SIGN_IN_RELAY_PROVIDERS 中某个 provider 的 CLI:Codex、Claude、Gemini(antigravity)和 Muse |
它将查询重放到主机上该 CLI 自己的回环监听器。Muse 设备页面没有回调:客户端只负责打开它。 |
link/start |
主机 agent 在其对话中打印出来的登录页面 | 它将查询重放到该页面的回环地址。 |
provider-account/start |
Codex 或 Claude 第一个之后的账户(PROVIDER_ACCOUNT_RELAY_PROVIDERS),新账户或已注销的账户。独立能力 provider-account-relay-v1 增加了这条路由(provider-account-relay-v1.ts) |
它会像 provider/start 一样,将查询重放到主机上该账户 CLI 的回环监听器。 |
时序¶
User's computer (client) Host (joined server)
------------------------ --------------------
POST mcp/start {config} ---------------------> McpHostSignIns.startRelay: start the sign-in,
open no tab, keep the page address
<--------------------- {flowId, expiresAt}
POST status {flowId} ---------------------> SignInRelayHost.status
<--------------------- {authorizeUrl, callback {port, path, state}, result}
Read port, path and state from authorizeUrl
(refuse a page with no state)
Listen on that port (or use the own
MCP listener on 28417 /mcp-auth, by state)
Show the page's host name; the user clicks
"Open sign-in page" (https only)
... the user signs in ...
Browser -> http://127.0.0.1:<port><path>?code&state
POST complete {flowId, query} ----------------> check state, give code to McpOAuth
<--------------------- {result: null} -> read status until result
组成部分¶
客户端如何接收回调¶
客户端按以下顺序尝试这些方式:
- 它自己的 MCP 授权监听器。 回调指定端口 28417 和路径
/mcp-auth,而客户端自己的 MCP 授权监听器正使用该端口。监听器会向SignInRelayClient.deliver传递一个没有启动任何本地登录的state。openbot://mcp-auth链接也会执行相同操作。 - 精确端口。 客户端在登录期间通过
listenLoopbackExact绑定127.0.0.1:<port>。它绝不会占用其他端口,因为页面会将浏览器导向宿主的端口。 - 粘贴的地址。 端口已被占用、回调不是回环地址,或者客户端是浏览器客户端。对话框会要求用户粘贴浏览器在末尾显示的地址。客户端只提取查询部分,并通过
complete发送。
没有回调的页面(例如 Muse 设备页面)就没有可供接收的内容。客户端会打开它并读取 status,直到宿主上的 CLI 结束登录。
不具备 sign-in-relay-v1 的宿主会回退到 mcp-sign-in-v1 的实时视图:宿主会在自己的私有标签页中打开页面,用户通过实时视图完成登录。两种能力都不具备的宿主会回应:登录必须在宿主机上完成。
规则¶
- 只有所有者或管理员可以使用中继路由。
requireAdmin会在每条路由上运行。 flowId是 128 个随机位。一个流程只会完成一次,且只对启动它的成员生效,并在SIGN_IN_RELAY_TTL_MS(10 分钟)后结束。未知流程、过期流程以及其他成员的流程都会返回相同的404。- 客户端只发送回调的查询部分。宿主会对照其自身登录流程生成的页面地址检查
state。客户端永远无法选择宿主端口、主机名或路径。 - 宿主只会为
127.0.0.1、localhost或[::1]上的http:地址(带端口)命名回调。 - 宿主选择页面,因此不诚实的宿主可能会请求用户浏览器中另一个账户的授权。因此,客户端会:
- 只打开
https:页面,并且只在用户点击显示页面主机名的对话框中的“打开登录页面”时才会打开; - 从页面的
redirect_uri读取回调端口和路径,而不是从宿主的callback中读取; - 拒绝没有
state的页面,因此其监听器只会传递本次登录的回调。 - 客户端监听器会拒绝
Host头不是回环名称的请求、不是 GET 或 HEAD 的方法,以及任何其他路径。它的页面是一条固定的目录消息。 - 授权码是机密信息。宿主会在传递前对其调用
registerSecretValue。任何日志行、分析事件或诊断导出都不会包含该授权码、state或查询内容。 - 授权码只通过加密的 Team 通道传输。账户 Worker 永远不会看到它。
- 重放投递会向宿主自身页面的回环地址发送一个 GET 请求。它不跟随重定向,只保留状态码并丢弃响应体。
link/start只接受带有state和回环redirect_uri(relayableSignInLink)的 https 页面,并且只有当agentId在过去一小时内的某条 Agent 消息将该确切地址作为完整链接包含(agentPrintedLink)时才接受。任何其他链接都会返回404,因此客户端无法让宿主向它选择的端口发送查询。重放只发送在宿主机上点击该链接时会发送的内容。- Agent 链接会立即打开,因为用户已点击它。桌面应用只有在宿主指定的页面正是用户点击的链接时才打开它;任何其他页面都会在未打开的状态下结束登录。浏览器客户端一如既往地在点击时打开链接,因为浏览器会阻止未经点击而打开的页面;只有当宿主指定的是同一页面时,它才接受粘贴的地址。
- 当桌面应用无法绑定端口时,以及在浏览器客户端中始终如此,
openMessageLink会返回该页面,对话中显示带有粘贴字段的McpSignInRelayDialog。pasteMessageLink在登录结束时结束;cancelMessageLink会在宿主上取消它。
新中继类型的安全检查清单¶
- [ ] 主机根据自身记录构建完整回调地址。客户端仅发送查询。
- [ ] 主机在传递前检查
state。客户端拒绝没有state的页面。 - [ ] 在用户看到其主机名并发出请求之前,不得打开该页面。
- [ ] 一个流程在截止时间前为一个成员只完成一次。
- [ ] 代码通过
registerSecretValue注册,且绝不写入日志。 - [ ] 路由使用
requireAdmin,且其编解码器位于可选能力中(而非 bootstrap 中)。 - [ ] 检查单次使用、对方成员、过期和状态不匹配。另请参阅 测试通过已加入的服务器进行登录。
添加提供方¶
提供方是编码代理 CLI,例如 Codex、Claude 或 Gemini。请先阅读 提供方。 描述符行和驱动是起点;类型检查器随后会指出其余大部分位置。
- 描述符。 将 id 添加到
AGENT_PROVIDERS,并在packages/contracts/src/agent-providers.ts中向AGENT_PROVIDER_DESCRIPTOR_TABLE添加一行:displayName、cliName、authKind、signInMessage、localCodeSignIn、skillFolders以及 其他字段。该表使用satisfies Record<AgentProviderId, ...>,因此缺少某行将无法编译。 不要添加到AGENT_IDS_RESERVED_AT_RELEASE:它是冻结的。 - 驱动。 在
src/backend/provider-drivers.ts中向BUILT_IN_PROVIDER_DRIVERS添加一行。选择登录方式: signIn:browser、cli-command、external、terminal、muse-device或acp-authenticate。codeSignIn(可选):codex-device、muse-device,或使用device、paste或link流程的cli。代码登录正是让已加入服务器现在就能登录的方式。 分派由provider-runtime.ts中的#signIn和#startProviderCode完成。复用这些辅助函数:acp-sign-in.ts、cli-code-login.ts、codex-login.ts和provider-terminal-login.ts。- 后端。
src/backend/cli.ts中的resolve<X>Cli、src/backend/process-confinement.ts中的状态路径、src/backend/agent/thread-items.ts和src/backend/agent/provider-diagnostics.ts中的providerLabel。 - 托管运行时(当 OpenBot 安装 CLI 时):
native-runtime.lock.json、scripts/agent-runtime-lock.ts,以及src/main/provider-runtime-descriptors.ts、provider-runtime-releases.ts和provider-runtime-manager.ts。 - 契约和预加载。
ipc-agent-status.ts中的AgentAuthState分支,然后是ipc-provider-admin.ts、src/preload/provider-runtime.ts和src/preload/agent-decoding.ts。 - 渲染器。
SetupProviderPicker.tsx、ProviderSettingsSection.tsx、app-defaults.ts,以及 预览文件mock-provider-runtimes.ts。 - 徽标。 位于
packages/brand/src/ProviderLogo.tsx中的ProviderLogoVariant联合类型及其 SVG,以及packages/brand/src/tokens.css中的颜色。 - Team API。 已发布能力的提供方列表是冻结的。已加入的服务器上的新提供方需要新的
providers-vN能力(见 提供方 中的“协议兼容性”),以及provider-visibility.ts中的对等规则。 - 文本和文档。
error/provider.ts中的错误键以及所有语言中的引导文本 (docs/i18n.md)。在 提供方 中添加一节,并添加changelog.d/<branch>.md文件。
工作示例:Codex 浏览器登录。 驱动行使用 signIn: { kind: "browser" }。
codex-login.ts
中的 CodexLoginFlow.startBrowser 会向 Codex app-server 请求 account/login/start,
获取 authUrl,并使用 openExternal 打开它。Codex CLI 自行处理回环回调。
对于另一台计算机,该行还包含 codeSignIn: { kind: "codex-device" }:主机启动设备码,
客户端将其显示出来。
在带有 sign-in-relay-v1 的已加入服务器上,Connect 使用 provider/start。
主机以 "relay" 模式运行相同的 startBrowser,因此 openPage 会保留 authUrl
且不打开标签页。客户端在自己的浏览器中打开该页面,并监听页面 redirect_uri
的端口(当前 Codex 版本中为 1455)。主机将回调查询重放到该端口上的 Codex 监听器。
要中继新的提供方,请在能力发布之前将其添加到 SIGN_IN_RELAY_PROVIDERS,
并为主机提供其页面:
- 遵循
BROWSER的 CLI:使用browser-capture.ts中的captureBrowser运行它。Claude 就是这样做的。 - 打印其链接的 ACP agent:使用
acp-authenticate捕获方式。Gemini 就是这样做的。 - 返回页面地址的 CLI(例如 Codex app-server):将它交给
openPage。
自行打开浏览器且不遵循 BROWSER 的 CLI 无法中继。请为它提供代码登录方式。
添加应用程序¶
应用程序是 Marketplace 安装的 MCP 服务器,可带有可选技能。源码位于
marketplace/plugin-catalog/
中,每个插件对应一个文件夹。
-
添加
marketplace/plugin-catalog/plugins/<slug>/plugin.json,并将该 slug 添加到catalog.json的order中。字段包括slug、name、tagline、description、category、creatorName、iconUrl、version、prompts、apps、skills(目前为[])以及政策 URL。类型见scripts/build-plugin-catalog.ts。 -
每个应用都有一个
server:http(带url),或stdio(带command和args)。它的auth描述用户如何连接: link:浏览器登录。OpenBot 是 OAuth 客户端(mcp-oauth-provider.ts中的McpOAuth):发现、动态注册、PKCE 和刷新。令牌保存在加密存储中。无需桥接程序。key:一个或两个字段,放入header(http)或env值(stdio)中。local:用户在运行服务器的计算机上执行的步骤。
通过 AppleScript 驱动 Apple 应用的 stdio 应用会在 macAutomation 中列出这些应用(calendar、contacts、mail、notes、reminders、messages、safari)。在运行服务器的 Mac 上,列表页面随后可以在安装时向 macOS 请求每个应用(mac-automation.ts)。移动端副本会省去该字段。
-
绝不要把机密放在目录中。用户在连接对话框中输入它。
-
构建并检查生成的文件:
构建会写入src/renderer/src/features/settings/marketplace-plugin-catalog.ts、apps/auth-api/src/lib/plugin-catalog.generated.ts、apps/mobile/src/features/marketplace/model/plugin-catalog.generated.ts和resources/plugin-catalog/。请勿手动编辑它们。移动端副本声明自己的类型,并省略厂商图标地址:手机从/plugins/icon/<slug>?app=<id>加载图标。 -
对于
http服务器,运行bun run test:mcp-catalog(mcp-catalog-smoke.ts)。它会在没有账户的情况下连接在线服务器,并写入.openbot-build/mcp-catalog-smoke/report.json。它需要网络,因此是手动运行,不是 CI 门禁。
冒烟测试会检查没有 auth 或使用 link 流程的 http 服务器。它会跳过 stdio 服务器以及仅包含 key 或 local 流程的服务器:这些服务器需要用户的密钥或计算机。仅当冒烟测试通过时,才添加或保留对应条目:
auth |
服务器必须 |
|---|---|
| None | 在无凭据的情况下应答 initialize。 |
link |
以 401 和 Bearer 质询拒绝 initialize。发布受保护资源元数据,指明授权服务器。该服务器的元数据列出 registration_endpoint、code_challenge_methods_supported 中的 S256 以及 code 响应类型。其端点使用 https 和公共地址。 |
没有 registration_endpoint 的 link 服务器会失败:每个用户都必须先注册一个登录应用。这种情况下应改用 key 流程,或者将其排除在外。冒烟测试只读取元数据。它从不注册客户端,也从不发送凭据。
没有自行注册 OpenBot 的服务器需要一个登录应用(sign-in app):管理员向提供商注册的 OAuth 应用。用户在 MCP 服务器的“登录应用”区域输入其客户端 ID(McpSignInAppSection.tsx)。回调 URL 为 http://127.0.0.1:28417/mcp-auth。在已加入的服务器上,主机保留该应用(mcp-sign-in-app-v1)。
工作示例:Linear(marketplace/plugin-catalog/plugins/linear/plugin.json):
"apps": [
{
"id": "app-linear-mcp",
"name": "Linear",
"description": "Issue search, triage, status updates, and issue creation, over Linear's MCP server with browser sign-in.",
"iconUrl": "https://linear.app/favicon.ico",
"server": {
"name": "linear",
"transport": "http",
"url": "https://mcp.linear.app/mcp",
"auth": [{ "id": "linear-oauth", "kind": "link", "label": "Sign in" }]
}
}
],
在本计算机上,“连接”会在浏览器中打开 Linear 的页面,授权通过 28417 端口返回。在带有 sign-in-relay-v1 的已加入服务器上,Marketplace 会在主机上安装该应用,并通过中继在本计算机的浏览器中打开页面。
添加连接器¶
连接器将 OpenBot 与外部账户连接起来,供所有智能体使用。每个连接器都有一个主进程服务、一个 IPC 契约、一个渲染器控制器和一个面板。Marketplace › 应用、聊天建议卡片和浏览器客户端读取同一个列表,因此连接器在它们每个上都显示相同的状态。
| 连接器 | 服务 | 登录 | 契约 |
|---|---|---|---|
| GitHub | github-connector-service.ts, github-device-flow.ts |
GitHub App 的设备流程,仅使用公共客户端 ID | ipc-github-connector.ts;已加入服务器:connectors-v1 GitHub 路由 |
| Slack | src/backend/messaging/slack/ |
账户 Worker 中的托管 OAuth,以 openbot://slack-workspace 结束并附带密封授权 |
slack-workspace-grant.ts、ipc-messaging.ts |
| Discord | src/backend/messaging/discord/ |
相同,以 openbot://discord-guild 结束 |
discord-guild-grant.ts、ipc-messaging.ts |
| Telegram | src/backend/messaging/telegram/ |
t.me 链接中的一次性链接代码。无 OAuth |
telegram-app.ts、ipc-messaging.ts |
| 1Password | onepassword-connector-service.ts |
用户的 op 会话创建只读服务账户,或用户粘贴令牌 |
ipc-onepassword-connector.ts;已加入服务器:onepassword-connector-v1(仅令牌) |
| Bitwarden | bitwarden-connector-service.ts |
一个仅保存在内存中的 bw 会话密钥;已加入的服务器使用 API 密钥和主密码登录 |
ipc-bitwarden-connector.ts;已加入的服务器:bitwarden-connector-v1 |
要添加一个连接器:
- 在
packages/contracts/src/ipc-<name>-connector.ts中添加 IPC 契约,在src/main/ipc/<name>-connector-handlers.ts中添加处理程序(阅读 src/main/ipc/AGENTS.md),并添加预加载解码器和预览 mock。 - 在
src/main/<name>-connector-service.ts中添加服务。将其机密保存在加密存储中,并在打印第一条日志行之前对每个 token 调用registerSecretValue。 - 优先使用设备码:它可以在任何计算机上以及通过任何已加入的服务器工作。深度链接(
openbot://...)只能到达打开该页面的计算机。将其类型添加到deep-link-router.ts,并让授权不进入渲染器。 - 将 ID 添加到
CONNECTOR_IDS,并将其条目添加到connectors.ts中的CONNECTORS。server是让已加入的服务器运行它的能力;当只有用户自己的计算机可以运行时(如保险库应用),则为null。connector-access.ts会读取它;没有任何界面拥有自己的门控。 - 将其名称、标语、Marketplace 类别和标记添加到
connector-catalog.tsx中的CONNECTOR_LISTINGS;将其面板添加到ConnectorDetail和ConnectorDetailPanel;将其行状态添加到connectorSummary。 - 添加一个控制器(位于
connector-controller.ts中的ConnectorController),并为其操作提供createConnectorActions,然后在connector-registry.ts的createConnectorSet中创建它。connect是 Marketplace 卡片上的一键 Connect;当面板必须先要求输入时,则将其省略。watch跟踪一个在主进程不发送事件的情况下会发生变化的状态。类型检查器会指出每个仍然缺少新 ID 的位置。 - 在所有语言中添加文本键,并添加一个
changelog.d/<branch>.md文件。当连接器向外发送新数据时,更新PRIVACY.md。
connectors-context.tsx 在桌面端创建两个注册表:一个用于当前屏幕上的服务器(Marketplace 和聊天卡片),另一个用于设置已打开的服务器。浏览器客户端在 WebWorkspace.tsx 中创建一个。注册表为每个连接器提供其访问状态:available(带有其控制器)、local-only、hidden、unsupported(例如没有 GitHub App 的 GitHub),或在首次读取之前为 loading。
在已加入的服务器上,可选能力 connectors-v1(connectors-v1.ts,主机路由 route-connectors.ts)承载 GitHub、Slack、Discord 和 Telegram:
- GitHub:主机运行设备码流程。客户端显示
userCode并打开verificationUri。主机会自行得知登录完成。 - Slack 和 Discord:主机返回安装页面和一个
nonce,并且不打开任何内容。客户端打开该页面。安装最终在客户端上以openbot://链接结束,该链接带有一个授权(grant),该授权被密封到主机的一次性密钥上。RelayedMessagingInstalls(relayed-messaging-installs.ts)仅当这台计算机为某台服务器打开了安装页面时,才会将带有messagingComplete的授权发送给该服务器,且只发送一次。客户端无法打开该授权。 - Telegram:客户端打开
t.me链接。没有回调。 - 没有任何路由会携带 token。概览会省略掉 bot token。
已加入服务器上的新连接器需要在一个新能力中提供新路由,因为 connectors-v1 一旦发布就会被冻结。
可选能力 discord-access-v1(discord-access-v1.ts,主机路由 route-discord-access.ts)可以读取和设置哪些 Discord 角色能够在主机的每个 Discord 服务器中使用 OpenBot,并通过 Signal(listRoles)列出该服务器的角色。主机将该列表保存在 projection_messaging_allowed_roles 中。空列表表示允许所有人。如果存在列表,MessagingService 会在作出反应或发帖之前忽略来自不具备任何这些角色的成员的提及。Signal 会在每条提及中附带作者的 role ID;来自旧版 Signal 的提及不会带有任何 role ID,因此有列表的服务器会忽略该提及。
可选能力 onepassword-connector-v1(onepassword-connector-v1.ts,主机路由 route-onepassword-connector.ts)承载 1Password。服务器通常没有用于 1Password 应用的屏幕,因此所有者或管理员会在 Marketplace › Apps › 1Password 中粘贴服务账户 token。该 token 会在 connect 中一次性发送给主机。主机用 1Password 校验该 token,并将其保存在自己的加密存储中。没有任何响应会携带它。状态和断开操作只作用于该主机。早于此能力的主机会隐藏 1Password。
已加入服务器上的 Bitwarden¶
可选能力 bitwarden-connector-v1(bitwarden-connector-v1.ts、主机路由 route-bitwarden-connector.ts)支持 Bitwarden 和自托管的 Vaultwarden。会话密钥只能解锁生成它的计算机上的 CLI 数据,因此主机使用自己的 bw 登录。所有者或管理员在“市场 › 应用 › Bitwarden”中输入服务器地址、个人 API 密钥和主密码。这些凭据只会在 connect 中一次性传给主机;不会有应答携带它们。connect 会立即返回 connecting,因为首次登录可能需要下载 bw 并同步保管库,耗时可能超过 15 秒的请求限制。客户端每 2 秒读取一次 status,直到状态改变。主机只有在解锁保管库并找到 Shared with OpenBot 文件夹后才会存储该记录,并在重启后再次从中解锁。主机必须通过 HTTPS 访问服务器地址;单主机上多个实例的实例防火墙会阻止私有地址。版本早于此能力的主机会隐藏 Bitwarden。
手机在“服务器设置 › 连接器 › Bitwarden”中使用相同的路由(apps/mobile/src/features/servers/components/bitwarden-connector-page.tsx)。与其他连接器一样,它在页面显示期间每 3 秒读取一次 status,并且不会保存 API 密钥或主密码的副本。对于版本早于此能力的主机,手机会将 Bitwarden 显示为“仅限桌面应用”,且不提供任何操作。
添加 Team API 能力¶
新的远程功能是一种新的可选能力。不要更改已发布的能力:已发布的 Team API 适配器是永久性的(参见 Team API 兼容性)。示例:sign-in-relay-v1,它沿用 mcp-sign-in-app-v1。
- 冻结的编解码器。 添加
packages/contracts/src/team-protocol/<name>-v1.ts。使用admin-wire.ts的辅助函数(adminRoute、fields、string(max)、nullable)构建每个路由。导出能力名称、路由和编解码器映射。将头部注释作为契约编写:说明每个路由的作用以及谁可以使用它。 - 包导出。 将
"./team-protocol/<name>-v1"添加到packages/contracts/package.json。 - 编解码器。 将编解码器映射展开到
optional-routes.ts中的CODECS。 - 能力列表。 将能力添加到
current.ts中的TEAM_CURRENT_CAPABILITIES,并将其从TEAM_BOOTSTRAP_CAPABILITIES和legacyTeamCapabilities中排除。较旧的对端读取的引导能力数量有限,因此新的可选能力不进入引导列表。 - 主机路由。 添加
src/main/team-api/route-<name>.ts。对于其他路径返回"unmatched",当该能力关闭时以400拒绝,并对管理员路由调用requireAdmin。在team-api-server.ts的TeamApiServer.#handle中注册它,并且仅当其依赖存在时,才在#protocolSupport中公布该能力。将依赖类型添加到team-api/dependencies.ts中,并在application-services.ts中创建它,然后通过host-service.ts传递它。 - 客户端门控。 将能力添加到
server-capabilities.ts中serverSupportsCapability的显式列表中,这样从未发送过该能力的较旧主机就不会被默认视为支持。在主进程中,使用remoteServers.supportsCapability(serverId, CAPABILITY)进行门控。 - 浏览器客户端。 将请求添加到
team-admin-requests.ts,并在 Web 客户端中根据主机的能力对它们进行门控。 - 引导(Bootstrap)。 确保该能力不在引导列表中。
- 文本。 错误消息使用
sourceText(key),键位于@openbot/i18n的所有语言中。
测试通过已加入服务器的登录¶
- 两个客户端:
bun run dev:test-client会启动服务、应用,以及一个带有自身配置文件的第二个客户端(参见 README.md: 命令)。从第二个客户端以管理员身份加入应用。在主机上添加一个带 OAuth 的 URL MCP 服务器,然后在客户端上点击登录。页面必须在客户端的浏览器中打开,并且主机必须显示该服务器已登录。 - 粘贴回退:用另一个程序占用客户端上的 28417 端口,然后再次登录。对话框会要求输入地址。从浏览器粘贴地址。主机必须登录成功。
- 浏览器客户端:打开
/app并登录到同一服务器。对话框显示“打开登录页面”和粘贴字段。 - 检查两台计算机的日志。它们不得包含
code=、code 或state。
使用 bun run dev:automation 进行快照和截图。不要操作另一个 worktree 的应用。
哪些内容无法中转¶
| 流程 | 原因 |
|---|---|
登录会返回提供方的公共 https: 地址 |
提供方会自行完成登录。主机只是等待。不会有任何内容返回到回环端口。 |
| 在主机上打开应用(如 Pi)的登录,Pi 会打开 Terminal | 该步骤发生在主机的屏幕上。没有可在其他位置打开的页面地址。 |
| 在桌面应用中批准操作的密钥库,如 1Password 或 Bitwarden | 批准操作发生在主机上的密钥库应用中。中继无法代为批准。已加入服务器上的 1Password 使用服务账户令牌(onepassword-connector-v1);Bitwarden 使用 API 密钥和主密码(bitwarden-connector-v1)。 |
Slack 开发返回地址(returnUrl) |
仅限开发构建。它会返回到主机上的 http://127.0.0.1:<port>/...,中继不会携带该地址。请使用托管安装。 |
| 非管理员成员的代理链接 | 中继路由仅限管理员使用。链接会原样打开,其回调无法到达主机。 |
| 命令、工具输出或评论中的代理链接 | 主机仅中继代理消息文本中包含的链接。 |
打开自身浏览器且不遵守 BROWSER 的 CLI |
主机永远不会获得页面地址。Grok、Cursor 和 Cline 使用一种代码类型。 |
| iPhone 应用 | 它不接受中继登录。它保持当前行为。 |
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot