技能与插件¶
要添加 Marketplace 应用,并了解其浏览器登录在已加入服务器上的工作方式,请阅读集成与登录。
技能文件夹与 MCP 配置¶
技能遵循 Agent Skills 规范:一个包含 SKILL.md 文件的 <name>/ 文件夹。YAML frontmatter 中有 name(即文件夹名)和 description(1 到 1024 个字符)。每个提供方的 CLI 在其各自的文件夹中查找技能:
| 文件夹 | 编写者 | 读取者 |
|---|---|---|
<workspace>/.agents/skills/ |
OpenBot、用户、代理 | Codex、Grok、OpenCode、Gemini、Cursor、Cline、Pi、Muse、自定义代理 |
<workspace>/.claude/skills/ |
OpenBot、用户、代理 | Claude Code、OpenCode、Cursor |
<workspace>/.opencode/skills/ |
用户、代理 | OpenCode |
<workspace>/.gemini/skills/ |
用户、代理 | Gemini |
<workspace>/.cursor/skills/ |
用户、代理 | Cursor |
<workspace>/.cline/skills/、<workspace>/.clinerules/skills/ |
用户、代理 | Cline |
~/.agents/skills/ |
用户 | Codex、Grok、OpenCode、Cursor、Cline、Pi;Claude(通过 OpenBot,见下文) |
~/.claude/skills/ |
用户 | Claude Code、OpenCode、Grok、Cursor |
~/.codex/skills/、~/.config/opencode/skills/ |
用户 | Codex、OpenCode |
受限代理(Grok、OpenCode、Gemini、Cursor、Cline、Pi、Muse 或自定义代理,且不在完全访问(Full access)模式下)无法写入工作区技能文件夹:src/backend/process-confinement.ts 将它们作为项目设置进行保护。
OpenBot 将安装的每个技能同时写入 .agents/skills/<slug> 和 .claude/skills/<slug>,因为 Claude Code 不读取 .agents/skills。它复制文件而不是创建链接。工作区中的 .openbot/skills-lock.json 记录文件哈希,.openbot/skills-disabled/ 存放已禁用的技能。捆绑的技能带有 .openbot-managed.json 标记。
src/main/skill-folder-discovery.ts 将七个工作区文件夹中的其他技能均列为 workspace 技能。该列表是只读的:OpenBot 从不写入、移动或删除这些文件夹,它们也不计入代理的技能限制。没有 SKILL.md 的文件夹不算技能。当技能的 SKILL.md 不符合规范,或者它位于代理提供方不读取的文件夹中时,该技能会获得 problem 状态。更换提供方时代理会保留其工作区,因此 .agents/skills 中的技能在改为 Claude Code 后会停止工作。带有 problem 的技能不会作为聊天标签提供。
OpenBot 不在技能面板中显示主目录文件夹。这些文件夹存放主机用户的技能,对所有代理都是一样的,而且每个提供方 CLI 都会在不另行通知的情况下更改其主目录文件夹规则。~/.agents/skills 是唯一的例外,因为它是 Agent Skills 工具共享的文件夹,因此每个提供方都能获得它。src/backend/home-skills.ts 读取它但从不写入;它列出每个包含有效 SKILL.md 的文件夹,最多 200 个。提供方描述符中的 homeSkills 说明了每个 CLI 如何获取这些技能:
native:CLI 自行读取该文件夹。OpenBot 不提供任何内容。plugin(Claude Code):claudeHomeSkillPlugin(src/backend/claude-workspace-sandbox.ts)在提供方状态文件夹中创建一个名为agents的插件,每个技能一个链接,每个会话都会获得它。文件夹名同时出现在$CLAUDE_CONFIG_DIR/skills(否则为~/.claude/skills)或工作区.claude/skills中的技能会被排除,因为npx skills会将技能链接到这两个文件夹。每组技能都有自己的文件夹,以哈希命名且永不更改,因此进行中的查询会保留其链接。Claude 将技能列为agents:<name>。instructions(Antigravity、Muse、自定义 ACP 代理):这些 CLI 不读取该文件夹,也没有对应选项。ThreadLifecycle向开发者指令提供技能列表,最多 100 个,包含每个描述的前 300 个字符及其SKILL.md的路径;当任务匹配时,模型会读取该文件。文件夹名位于提供方工作区技能文件夹中的技能会被排除。模型是否使用某个技能取决于模型本身。
$ 列表(listInstalledForChatTags)将这些技能排在其他技能之后添加,id 为 home:<slug>,并使用现有的 workspace 来源,以便旧客户端能识别它们。列表中已有的 slug 会被排除。桌面端只读的技能视图读取相同的路由并排除 home: id(isHomeSkillId),分析中的技能计数也是如此。手机端在代理信息中显示它们及其 ~/.agents/skills/<slug> 位置。home:<slug> 标签的提供方文本会显示该技能 SKILL.md 的路径(displayMessageReferences);而人类以及已发布的 Team API 适配器只会看到名称本身。
MCP 服务器不使用文件夹。SQLite 中的 projection_mcp_servers 是整台计算机的事实来源。不存在共享的 MCP 文件格式:Claude Code 读取 .mcp.json 和 ~/.claude.json,Codex 读取 config.toml,OpenCode 读取 opencode.json,Cursor、Cline 和 Gemini CLI 读取各自的文件夹。OpenBot 不写入这些文件中的任何一个。它在会话启动时将服务器提供给每个提供方。Claude 以 strictMcpConfig 启动,因此会忽略 .mcp.json 及其用户设置(参见 plans/003-mcp-works-on-a-clean-machine.md)。面板会掩蔽请求头和环境变量值,src/backend/mcp-redaction.ts 会将它们从日志中移除,OAuth 令牌则存放在 safeStorage 中。
1Password 连接器是内置的,没有对应的 SQLite 行。src/main/onepassword-connector-service.ts 会运行一次用户的 op CLI,以创建“Shared with OpenBot”保险库和 read_items 服务账户,或者接受粘贴的服务账户令牌。在 Connect 之前,页面会显示 checkSetup() 读取的三个设置步骤:2.18 或更高版本的 CLI(用户 PATH 中的自带版本,否则使用 src/main/onepassword-cli-installer.ts 下载的副本,按目标平台固定 SHA-256,放入 <userData>/provider-state/1password-cli)、1Password 应用的 CLI 集成(op account list 至少返回一个账户)以及 Connect。该服务仅在 openbot-onepassword-connector-v1.json 中保留令牌,并使用 safeStorage 加密。它通过 @1password/sdk 读取保险库,并实现 PasswordVault(src/backend/password-vault.ts)。开发者指令只在 PasswordVault.connected() 为 true 时才向代理介绍该保险库;该状态在每次会话开始和恢复时读取,因为大多数用户没有保险库。代理服务在两处使用它:openbot_browser.list_logins 返回为标签页的 HTTPS 站点保存的登录信息(id、标题、用户名);AttentionRegistry 会回应针对已保存登录信息的 submit_secret 密码或验证器请求,通过与安全卡片相同的 prepareSecret 路径填写该信息,且不显示卡片。在代理于本次应用会话期间运行过 evaluate 的来源上,BrowserHost 会报告 agentScriptedOrigin,改为打开卡片,因为代理的脚本可能读取到已填充的字段。登录信息按 1Password 的自动填充规则匹配,在可注册域上进行计算,私有后缀(如 github.io)也计入。代理和提供方永远不会收到令牌、密码或验证码。
Bitwarden 连接器也实现了 PasswordVault。它使用已安装的 bw 可执行文件,通过绝对 PATH 条目或常见安装目录解析为绝对路径。它在不使用 shell 且环境经过白名单限制的情况下运行固定参数。用户在 OpenBot 外部登录并解锁 Bitwarden 后提供 CLI 会话密钥。OpenBot 不保留连接文件,也从不询问主密码。CLI 拥有其加密缓存和服务器配置。每个操作都有 30 秒的截止时间和输出限制。发生错误时丢弃所有 CLI 输出。断开连接会取消正在进行的调用,迟到的调用无法从旧会话返回凭据。连续八小时没有成功的密码库调用将结束连接。
连接要求恰好有一个名为 Shared with OpenBot 的文件夹。其 id 限定后续读取的范围。每次填充都会同步并检查当前条目、其文件夹、其保存的 HTTPS 来源、其删除状态以及主密码提示设置。默认、域和主机 URI 规则使用精确的 HTTPS 来源匹配。不支持其他 URI 规则;这些登录使用手动机密卡片。会话密钥可以解密更广泛的密码库;文件夹限制由 OpenBot 而非 Bitwarden 强制执行。所有本地代理共享此授权。提示注入仍可在其自身站点上使用已批准的账户;此功能不会在登录后授权或限制操作。
任务所需的 API 密钥(例如用于脚本或服务)使用 openbot.request_api_key。路由器构建一个包含一个必填机密字段的 form UI 块(src/backend/agent/prompts.ts 中的 apiKeyFormSpec),因此每个显示 ask_ui 的客户端都会显示它;没有 IPC 或 Team API 契约发生变化。用户提交后,AttentionRegistry 会在提示解析完成前使用 AgentApiKeys(src/backend/agent-api-keys.ts)将值保存为 <userData>/agent-api-keys/<agentId>/<NAME>:这是 0700 文件夹中的一个 0600 文件,位于所有工作区之外,且未加密。代理只获得名称和路径,并在自己的命令中读取该文件。只有服务器所有者或管理员可以应答。该值在保存时和启动时都会登记为日志脱敏项,存储的块不保留机密字段值。list_api_keys 和 remove_api_key 是反向操作,删除代理会删除其文件夹。该表单在 Slack、Discord 和 Telegram 聊天中被拒绝。
password-vault-router.ts 合并两个管理器的元数据,并为 Bitwarden id 添加 bitwarden: 前缀。现有的 1Password id 保持不变。列表失败会停止自动账户选择,而不是隐藏某个来源并选择不同的账户。Main 仅通过本地 Bitwarden IPC 组返回连接状态。共享面板用于桌面 Marketplace 和服务器设置。
一台计算机的会话密钥无法打开另一台计算机的密码库,因此已加入的服务器使用自己的 CLI 登录(bitwarden-connector-v1,参见 integrations)。所有者或管理员在桌面或浏览器客户端中输入服务器地址(Bitwarden 或自托管的 Vaultwarden,仅 https:)、个人 API 密钥(client_id、client_secret)和主密码。主机将它们保存在 openbot-bitwarden-connector-v1.json 中(使用 safeStorage 加密),并将其 CLI 数据保存在 <userData>/provider-state/bitwarden-host(BITWARDENCLI_APPDATA_DIR)中,与用户的任何 bw 配置分开。如果 PATH 中没有 bw,它会将 src/main/bitwarden-cli-installer.ts 固定的版本(每个目标对应一个 SHA-256)下载到 <userData>/provider-state/bitwarden-cli。它运行 bw config server、bw login --apikey 和 bw unlock --passwordenv;机密仅在环境中传递给 bw。重启或空闲八小时后,下一次密码库调用会从存储的记录中再次解锁。断开连接会删除记录和 CLI 数据。手机显示该连接器但无需任何操作。
GitHub 连接器是内置的,没有 SQLite 行。src/main/github-connector-service.ts 使用设备流程登录 openbotgit GitHub App,该流程只需要公共 Client ID,并将令牌保存在 openbot-github-connector-v1.json 中,使用 safeStorage 加密。在连接期间,McpGateway.enabled() 会添加 openbot-github 服务器(api.githubcopilot.com/mcp/),mcpAuthorization() 在每次交接时为其提供新的 bearer 令牌。用户添加的名为 github 的已启用服务器优先。对于 gh 和 git,服务将令牌写入 <userData>/provider-state/github(模式 0600),每个提供程序都会获得 GH_CONFIG_DIR 和读取该文件的 GIT_CONFIG_* 凭据助手。环境中只包含路径,绝不包含令牌。Codex 通过线程配置中的 shell_environment_policy.set 获取这些值。
用户令牌会让 GitHub 显示“user with OpenBotGit”。要显示 openbotgit[bot],桌面端会将用户令牌发送到账户 Worker 上的 POST /v1/github/installation-tokens(apps/auth-api/src/server/github-installation-tokens.ts)。Worker 持有应用的私钥(GITHUB_APP_PRIVATE_KEY,PKCS #8)并签署应用 JWT。它为这个应用的每个安装生成一个安装令牌,仅限于用户可以推送、维护或管理的仓库,并且不存储任何内容。没有密钥时它返回 503,桌面端保留用户令牌。src/main/github-bot-tokens.ts 在第一个令牌过期前十分钟更新该集合。
git:助手使用useHttpPath运行,并从provider-state/github/repositories获取该仓库的机器人令牌,否则使用用户令牌。- MCP:
src/main/github-mcp-proxy.ts是一个带有机密 bearer 的回环 MCP 服务器。它为每个令牌使用一个上游客户端转发到api.githubcopilot.com/mcp/,因为 GitHub 将 MCP 会话绑定到其令牌。带有owner和repo参数的工具调用使用该仓库的机器人令牌。端口和机密保存在加密记录中,因此恢复的 Codex 会话会保留其 URL 和标头。 gh每个主机有一个令牌,因此它以用户身份运行。
一个 agent 使用 bot token 打开的 pull request 会以 openbotgit[bot] 为作者,因此发起该请求的用户可以批准它。只需要一次审批的分支规则随后会在没有第二人的情况下通过。
本地技能库¶
src/main/local-skill-library.ts 负责管理应用 user-data 目录下的不可变修订版本,路径为 local-skills/<local-skill-uuid>/<revision>/bundle.zip。只有 bundle 写入完成后,暂存目录才会被重命名;读取操作会忽略未发布的暂存目录。修订版本会被串行化处理,并与调用方期望的修订版本进行核对。无需进行 SQLite 迁移。
共享的包校验器同时处理本地和 marketplace 包。现有的安装器负责每个 agent 的文件、哈希、禁用状态存储以及两个 provider 目录。本地安装会跳过 marketplace 下载和收据请求。安装操作按 agent 串行执行;库修订版本不会更新已安装的副本。
一个 pack 是一个 bundle,其中包含自己的技能,路径为 skills/<slug>-<name>/SKILL.md。安装器会将其作为同级文件夹 <slug>-<name> 写入两个 provider 目录,这样 provider 能找到它,$<slug>-<name> 也会列出它;并且它们都归在 pack 的同一个锁条目下:安装、更新、禁用、启用和移除会一起作用于每个文件夹,只需一次下载和一次安装收据。
后端本地技能工具从调用方 provider 会话中获取 agent。主进程 IPC 会独立于发送方校验来验证本地库输入。渲染器通过该桥接读取本地预览。已发布的 Team API 适配器保持不变;本地创建和修订不会作为远程操作暴露。
插件分发¶
一个插件是一位开发者的 bundle:一个 MCP server,以应用形式展示,驱动它的技能,以及列表文本。可用插件目录是一个静态文件集,由 Account Worker 在 openbot.run 上无需账号即可提供;主进程会在 user-data 目录中保留一份副本,而不是放在 SQLite 中,因为远程目录是缓存,不是事实来源。安装会将应用保存为宿主全局的 MCP server,并将固定技能安装到所选 agent 中。openbot.run/plugins/<slug> 的分享链接会打开一个公开页面,而 openbot://plugins/<slug> 会在应用中打开该列表;两者都不会安装任何内容。
关于目录结构、获取与缓存规则、安装与卸载顺序、deep link 解析器规则以及安全审查,请参阅 plugin distribution and sharing。该设计中有三个部分目前已在运行。Apps 标签页会将列表中的固定技能安装到所选 agent,并将其应用保存为宿主全局的 MCP server。这些链接都能正常工作:openbot.run/plugins 和 openbot.run/plugins/<slug> 是公开站点上的页面,openbot://plugins/<slug> 会在应用中打开该列表,这是 src/main/deep-link-router.ts 除邀请外能识别的链接种类之一。两侧都读取同一个目录,该目录由 marketplace/plugin-catalog/ 生成,因为如果列表在页面上说一套、在应用里说另一套,那就会是两个目录。Apps 标签页也会卸载列表。目录文件、提供它们的 Worker 路由以及主进程中的缓存仍处于设计阶段。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot