可扩展性:技能、MCP、插件与内建扩展
30 秒导读: OpenWork 是一个跑在你自己文件上的 AI agent 桌面应用。裸模型只会说话——它不知道你的公司规范、连不上你的 Notion、也没有画图的手。本章讲 OpenWork 最核心的产品价值:用四条互补的路给 agent 装能力(技能 / MCP / 插件 / 内建扩展),以及它们如何全部落到
opencode.json、.opencode/目录、和服务端 runtime 配置这三处载体上。
本章聚焦「 能力从哪来、装到哪去、agent 运行时怎么看见」。想先了解 OpenWork 的整体三 sidecar 架构,看 02-orchestrator;想了解承载这些读写 API 的服务端本身,看 03-openwork-server。
1. 这是什么(零基础也能懂)
一句话定义: OpenWork 底层跑的是 OpenCode 引擎,它天生支持技能、MCP、插件三种扩展点;OpenWork 在其上再加一层「内建扩展」和「一键安装」的 UI,把「给 agent 装能力」变成点几下按钮的事。
为什么需要它。 一个只有基础工具(读文件、跑命令、搜网页)的 agent,遇到下面这些请求会卡住:
- "按我们团队的 PR 规范来改" —— 它不知道你的规范。
- "查一下我 Google 日历上周五有没有空" —— 它连不上 Google。
- "把这个功能画成一张架构图" —— 它没有生成图片的能力。
四条路,各补一块。 OpenWork 把「装能力」这件事拆成四种手段,每种补的东西不一样:
| 扩展路径 | 白话:它给 agent 补什么 | 典型例子 |
|---|---|---|
| 技能 Skills | 一段领域知识 / 操作手册,按需注入 | "我们的发布流程"、"如何写符合规范的 commit" |
| MCP | 一条通往外部系统的连线(带工具) | 连 GitHub、Linear、Notion、自建服务 |
| 插件 Plugins | 一段改造引擎本身的代码 | 拦截请求、加自定义工具、改 prompt |
| 内建扩展 Extensions | OpenWork 随包带的托管能力 | Google Workspace、OpenAI 生图 |
一句话直觉: 把 agent 想成一个新来的实习生。技能是给他的《员工手册》;MCP 是给他公司各系统的门禁卡;插件是直接改造他的工位设备;内建扩展是公司已经配好、开箱即用的那几台机器。
用起来什么样。 用户在 OpenWork 的「Extensions」设置面板里操作,底层每一步都归结为对三处载体的读写:添加一个技能 = 往 .opencode/skills/<name>/SKILL.md 写一个文件;连一个 MCP = 往 runtime 配置里加一条 mcp 记录;装一个插件 = 往 opencode.json 的 plugin 数组里加一项。UI 分区见 apps/app/src/react-app/domains/settings/pages/extensions-view.tsx:10 的 ExtensionsSection = "all" | "mcp" | "skills" | "plugins"。
2. 顶层全景(它大概怎么转)
2.1 三处载体
理解本章的关键,是先记住能力最终落到哪三处——所有扩展路径都在往这三处之一写:
| 载体 | 位置 | 谁在读 | 承载什么 |
|---|---|---|---|
.opencode/ 目录文件 | 工作区内(受版本控制) | OpenCode 引擎按目录约定加载 | 技能、命令、agent、目录级插件文件 |
opencode.json | 工作区 / 全局 | 引擎启动时读 | plugin 数组、mcp 映射(用户手写的) |
| 服务端 runtime 配置 | 服务端专属 SQLite(runtime DB) | 服务端构建后注入给引擎 | OpenWork 通过 UI 添加的 MCP / 插件 |
前两处是 OpenCode 原生的、用户可手写、可提交进 git;第三处是 OpenWork 私有的——UI 加的 MCP 不直接写用户的 opencode.json,而是存进 runtime DB,再在构建引擎配置时合并注入(见 openwork-runtime-config.ts:94 的 mcp: runtimeMcpMap(runtimeConfig))。这样「用户手写的配置」和「OpenWork 帮你加的」互不污染。
2.2 一张图:四条路如何汇入引擎
怎么读这张图:左边是四条扩展路径(用户视角),中间是它们各自落地的载体,右边是最终消费者——OpenCode 引擎。从左到右是「安装 → 落地 → 生效」。
用户操作(UI/手写) 落地载体 消费者
┌─────────────┐
│ ① 技能 │──写文件──▶ .opencode/skills/<n>/SKILL.md ─┐
└─────────────┘ │
┌─────────────┐ │
│ ② MCP │──写 DB──▶ runtime 配置(mcp 映射) ────────┤
└─────────────┘ │ ▼
┌─────────────┐ │(构建时合并) ┌──────────────────┐
│ ③ 插件 │──写 DB/json─┴──▶ plugin 数组─▶│ OpenCode 引擎 │
└─────────────┘ │ (buildOpenwork- │
┌─────────────┐ │ RuntimeConfig) │
│ ④ 内建扩展 │──随包内建──▶ openwork_extension_* 工具 ──────┘
└─────────────┘ (服务端 action 注册表)
MCP 那条线还有一个「热同步」旁路:UI 加 MCP 后,除了写 runtime DB,服务端还会立刻 POST /mcp 把这一条推给正在运行的引擎(server.ts:2166),不必等引擎重启就能连接。见 §3.2。
2.3 部件一句话职责
| 部件 | 干什么 | 文件 |
|---|---|---|
skills.ts | 扫描/读写工作区与全局技能 | apps/server/src/skills.ts |
skill-hub.ts | 从远程 Hub 仓库拉取并安装技能 | apps/server/src/skill-hub.ts |
mcp.ts | 列出/增删/开关 MCP(写 runtime DB) | apps/server/src/mcp.ts |
plugins.ts | 列出/增删 OpenCode 插件 | apps/server/src/plugins.ts |
cloud-plugins.ts | 安装 marketplace 云插件包 | apps/server/src/cloud-plugins.ts |
claude-plugin-bundle.ts | 把 Claude Code 插件仓库转成云插件包 | apps/server/src/claude-plugin-bundle.ts |
extensions/index.ts | 内建扩展 action 的注册表与派发 | apps/server/src/extensions/index.ts |
openwork-runtime-config.ts | 构建引擎可见的最终配置(注入插件+MCP) | apps/server/src/openwork-runtime-config.ts |
3. 核心机制(逐个机制,由浅入深)
3.0 基线:扩展之前 agent 免费拿到什么
在讲四条扩展路之前,先明确基线:agent 不装任何扩展时,已经有一整套内建工具。前端在 apps/app/src/lib/build-in-tools.ts 里为这些工具逐个定义了类型和识别函数,用来渲染每种工具调用的 UI。
这份清单就是「裸 agent 的能力边界」:bash、read、write、edit、grep、glob、webfetch、websearch、task(子 agent)、lsp、apply_patch、todowrite,以及一个特殊的 skill(见 build-in-tools.ts:351 的 SkillToolPart)。
注意 skill 也在这份内建清单里——这说明「调用技能」本身是引擎的一等工具,而技能 内容才是可扩展的部分。四条扩展路做的,都是在这条基线之上追加能力。
3.1 技能 Skills —— 给 agent 的操作手册
要解决的小问题: 怎么把「一段领域知识/操作步骤」按需喂给 agent,而不是塞满上下文。
思路: 一个技能就是一个文件夹,里面有一个 SKILL.md,frontmatter 写 name 和 description,正文写操作手册。引擎按 description 判断相关性,需要时才用 skill 工具把正文读进来。
技能长什么样(示意,非源码):
---
name: release-checklist
description: Use when cutting a new release — bumps version, updates changelog.
---
# 发布流程
1. 跑 `pnpm test`
2. 更新 CHANGELOG
3. 打 tag …
发现:多来源、多约定的扫描。 OpenWork 不只认自己的目录,还要兼容 Claude Code、通用 agent 等约定。listSkills 会沿工作区向上直到 .git 边界逐层找项目技能,再可选地找全局技能(skills.ts:119 的 listSkills):
| 作用域 | 扫描的目录 |
|---|---|
| 项目 | <root>/.opencode/skills、<root>/.claude/skills |
| 全局 | ~/.config/opencode/skills、~/.claude/skills、~/.agents/skills、~/.agent/skills |
每个目录还支持两种布局:扁平的 skills/<name>/SKILL.md,以及领域分组的 skills/<domain>/<name>/SKILL.md——listSkillsInDir 找不到直接的 SKILL.md 时会再往下钻一层(skills.ts:95-113)。同名技能按扫描顺序去重取先见者(skills.ts:140-145)。
写入。 用户导入/新建技能时,upsertSkill 一律写到项目的 projectSkillsDir,即 .opencode/skills/<name>/SKILL.md(skills.ts:189;路径定义见 workspace-files.ts:20)。写入前 buildSkillContent 会校验名字、补齐 frontmatter,并强制 frontmatter 的 name 与 payload 一致(skills.ts:154)。
从 Hub 远程安装。 skill-hub.ts 让用户从一个 GitHub 仓库(默认 different-ai/openwork-hub,见 skill-hub.ts:13 的 DEFAULT_HUB_REPO)浏览并一键安装技能。installHubSkill(skill-hub.ts:182)的关键细节:
- 用 GitHub trees API 递归列出
skills/<name>/下所有文件(不止SKILL.md),逐个下载写入——所以带辅助脚本/资源的技能也能装全。 - 路径穿越防护:每个目标路径都过
resolveSafeChild(skill-hub.ts:172),确保解析后仍在技能目录内,拒绝..。 - 目录列表带 5 分钟内存缓存(
CATALOG_TTL_MS),避免频繁打 GitHub API。
3.2 MCP —— 通往外部系统的连线
要解决的小问题: 怎么让 agent 用上 GitHub、Notion、或你自建服务的工具,而这些系统各有各的鉴权。
思路: MCP(Model Context Protocol,模型上下文协议)是一套标准,一个 MCP server 对外暴露一组工具;引擎作为 client 连上去就能用这些工具。OpenWork 负责「配置这条连线 + 走完鉴权」。
三层来源、就近覆盖。 listMcp(mcp.ts:41)把 MCP 分三个来源合并,优先级从低到高:
来源(source) | 来自哪 | 谁写的 |
|---|---|---|
config.global | ~/.config/opencode/opencode.json 的 mcp | 用户手写 |
config.project | 工作区 opencode.json 的 mcp | 用户手写 |
config.remote | 服务端 runtime DB 的 mcp 映射 | OpenWork UI 加的 |
同名时项目覆盖全局、runtime 覆盖项目(mcp.ts:53-83)。还有一个巧妙细节:isMcpDisabledByTools(mcp.ts:34)会检查 tools.deny 里有没有 mcp.<name>、mcp.* 等 glob——用户可以不删 MCP、只在 deny 列表里把它「静音」,UI 会把这类标成 disabledByTools。
UI 加的 MCP 写哪。 addMcp / removeMcp / setMcpEnabled 全部只动 runtime DB(mcp.ts:88/104/120,底层是 writeRuntimeOpencodeConfig),不碰用户的 opencode.json。setMcpEnabled 特意用「按路径更新」而非整块覆盖,以保留条目内的行内注释(mcp.ts:118-120 的注释,提到 #1444 回归)。
OAuth 流程(最有工程含量的一支)。 远程 MCP 常需要 OAuth。这条流程由引擎驱动,OpenWork 桌面端弹一个模态框(apps/app/.../connections/mcp-auth-modal.tsx)扮演「浏览器」。以 mcp.oauth-flow.e2e.test.ts 佐证的完整链路:
怎么读:每一步命中即进入下一步;虚线是「远程工作区 loopback 打不通」时的手动兜底。
引擎 OAuth 提供方(MCP server)
│ POST /mcp/<name>/auth
├───── 发现 + 动态客户端注册(DCR)──────▶ POST /register
│◀──── 返回 authorizationUrl(带 PKCE S256 challenge)
│
用户打开 authorizationUrl ─────────────────▶ GET /authorize
│◀──── 302 重定向到引擎 loopback callback(带 code)
│
├───── 用 code 换 token(PKCE 验证)───────▶ POST /token
│◀──── access token(持久化到 mcp-auth.json,跨重启复用)
│
└───── 已鉴权的 MCP 握手 ──────────────────▶ POST /mcp ✔ connected
┆ (loopback 不可达时) POST /mcp/<name>/auth/callback ← 手动兜底
测试断言了几处关键行为:授权 URL 必带 code_challenge_method=S256(PKCE 防截获,mcp.oauth-flow.e2e.test.ts:163);token 落盘到 mcp-auth.json 供跨重启复用(:210);登出 DELETE /mcp/<name>/auth 会删除存储的 token 并断连(:228)。无 OAuth 的普通远程 MCP 则简单得多,mcp.remote-connect.e2e.test.ts 佐证:addMcp → listMcp → removeMcp 一条龙,只动 runtime 配置。
引擎热同步(engine sync)。 runtime DB 是「真相源」,但正在跑的引擎实例未必立刻知道。syncRuntimeMcpToOpencodeEngine(server.ts:2828)负责把 runtime MCP 推进引擎:
- 逐条
POST /mcp,单条失败不阻断后续——一个坏 MCP 不能拖垮其余条目的注册(server.ts:2851-2858)。 - 对 5xx/网络错误重试一次(引擎常在 dispose 后短暂重建中),4xx 不重试(
postMcpEntryWithRetry,server.ts:2883)。 - 把最后一次同步结果按工作区记下来(
recordEngineMcpSyncResult),GET /workspace/:id/mcp会带上engineSync字段(server.ts:2144),让 UI 能解释「为什么这个明明启用的 MCP 却显示未连接」。 - 启动时
syncAllWorkspacesRuntimeMcpToEngine(server.ts:2949)补推所有工作区——因为注入的 runtime 配置文件只覆盖workspaces[0],其余工作区的 MCP 靠它才对引擎可见。
mcp.engine-sync.e2e.test.ts 用一个 mock opencode server 佐证了这些重试/失败上报路径。
3.3 插件 Plugins —— 改造引擎本身
要解决的小问题: 技能只是知识、MCP 只是外连,想改引擎行为(拦请求、加工具、改 prompt)怎么办?
思路: 插件是 OpenCode 原生的扩展方式(README 原话:插件是扩展 OpenCode 的 native 方式)。在 opencode.json 的 plugin 数组里列一个 npm 包名或文件路径,引擎加载它,插件导出的钩子就生效了。
最小配置(README opencode.json 示例):
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-wakatime"]
}
四种 来源、有加载序。 listPlugins(plugins.ts:53)聚合插件并给出 loadOrder:
来源(source/scope) | 来自哪 |
|---|---|
config / project | 工作区 opencode.json 的 plugin 数组 + runtime DB 的插件列表 |
dir.project | <root>/.opencode/plugins/*.js|.ts |
dir.global | ~/.config/opencode/plugins/*.js|.ts |
加载序固定为 ["config.global", "config.project", "dir.global", "dir.project"](plugins.ts:85)。
规格归一化去重。 插件规格五花八门(@scope/pkg@1.2、file:...、/abs/path、裸包名),normalizePluginSpec(plugins.ts:11)把版本号/协议前缀剥掉再比,避免同一插件写两遍。addPlugin 也用它判重后才追加进 runtime 插件列表(plugins.ts:89)。
OpenWork 自己注入的内建插件。 构建引擎配置时,plugin 数组头部会硬塞几个 OpenWork 私有插件,再接上用户的 runtime 插件(openwork-runtime-config.ts:85-92):
plugin: [
"opencode-chrome-devtools", // 浏览器自动化
openworkExtensionsPreviewPluginPath(), // 暴露 openwork_extension_* / openwork_ui_* 工具
openworkCapabilitiesKnowledgePluginPath(),
openworkAnthropicAdaptiveThinkingPluginPath(),
openworkAnthropicToolSchemaPluginPath(),
...runtimePluginList(runtimeConfig), // 用户装的
]
其中 openworkExtensionsPreviewPluginPath() 指向的那个插件,正是把「内建扩展」接进 agent 的桥梁——见 §3.5。这几个内建插件的解析路径要兼顾开发态(.ts)和 Electron 打包态(app.asar 外的 opencode-plugins/*.js),逻辑在 openwork-extensions-plugin-path.ts:17 的 openworkPluginPath。
3.4 云插件包与 Claude 插件兼容 —— 一键装一整包
要解决的小问题: 技能、MCP、命令、agent 往往成套出现(一个团队的整套配置)。逐个装太累,能不能一个链接装一整包?
思路: 云插件(marketplace package)把「若干 配置对象」打成一个 CloudPluginResolved,installCloudPlugin(cloud-plugins.ts:596)遍历其成员,按类型分别落地。
命名空间隔离。 一整包的东西全部装到 <plugin-namespace>/ 子目录下,避免和你已有的技能/命令撞名。落地路径由 getPluginObjectInstallPath(cloud-plugins.ts:239)按对象类型决定:
| 对象类型 | 落地路径 |
|---|---|
| skill | .opencode/skills/<ns>/<name>/SKILL.md |
| agent | .opencode/agents/<ns>/<name>.md |
| command | .opencode/commands/<ns>/<name>.md |
| mcp | 走 addMcp 写 runtime 配置(不落文件) |
注意 MCP 成员不写文件,而是复用 §3.2 的 addMcp(cloud-plugins.ts:614-627)——所以云插件里的 MCP 也自动走 runtime DB + 引擎热同步那套。卸载 removeCloudPlugin(cloud-plugins.ts:703)按安装时记下的文件清单精确回滚:文件删文件,MCP 调 removeMcp。
兼容 Claude Code 插件仓库。 很多现成插件是 Claude Code 格式(.claude-plugin/plugin.json + .mcp.json + skills//commands//agents/)。claude-plugin-bundle.ts 的巧思是:不新写一套安装逻辑,而是把 GitHub 仓库解析成同样的 CloudPluginResolved 形状,从而白嫖 installCloudPlugin 的命名空间、frontmatter 翻译、MCP 注册、卸载全套(见文件顶部注释,claude-plugin-bundle.ts:1-12)。
resolveClaudePluginBundle(claude-plugin-bundle.ts:267)处理了不少 现实脏活:
- URL 解析容忍
github.com/owner/repo、owner/repo、.git后缀、/tree/<ref>/<subdir>,并只允许 github.com(parseClaudePluginSource:71)。 - 分支名可含斜杠(
release/v1),/tree/<...>里 ref 和子目录的边界因此有歧义——resolveRefAndTree逐个候选去撞 trees API,取第一个能解析的(:159)。 - 定位插件根:给定子目录、仓库根、或最浅的含
plugin.json的目录;同深度出现多个则报「歧义,请在 URL 里指定目录」(locatePluginRoot:201)。 - 不支持的部分明确降级并告警而非静默:声明了
hooks、用了${CLAUDE_PLUGIN_ROOT}的 MCP、技能带SKILL.md以外的额外文件——都推进warnings让用户知情(:291、:344、:331)。
UI 侧对应 apps/app/.../connections/modals/claude-plugin-import-modal.tsx 与 add-mcp-modal.tsx。
3.5 内建扩展 Extensions —— 随包带的托管能力
要解决的小问题: Google Workspace、生图这类能力,鉴权和 API 调用都复杂,不该让每个用户自己配 MCP。OpenWork 想开箱即用。
思路(两段桥): 内建扩展的能力不作为普通工具直接给 agent,而是通过一个「元工具」桥接。这样 agent 的工具列表保持精简,需要时 再按需发现:
agent 想干活 → openwork_extension_list_actions(发现有哪些扩展 action)
→ openwork_extension_call(extensionId, action, args)
│ (OpenCode 插件, 在引擎内)
▼ HTTP POST /experimental/extensions/call
openwork-server 的扩展注册表
│ 按 extensionId 派发
┌─────────┴──────────┐
▼ ▼
google-workspace openai-image-generation
(日历/Gmail/Drive/Chat) (gpt-image-2 生 PNG)
第一段桥:引擎内的元工具。 openwork-extensions-preview.ts 是一个 OpenCode 插件(就是 §3.3 里被硬注入的那个),它:
- 通过
experimental.chat.system.transform往系统提示里塞一句「不确定能不能做时,先查 OpenWork 扩展」(openwork-extensions-preview.ts:204)。 - 暴露
openwork_extension_list_actions/openwork_extension_call两个工具,底层打服务端的/experimental/extensions/*(:209、:224)。 - 顺带暴露
openwork_ui_*一组工具,让 agent 能反向控制 OpenWork 桌面 UI(开设置、加 provider、读跨会话历史),经一个带 token 的本地 UI bridge(discoverUiBridge:91)。
第二段桥:服务端的 action 注册表。 extensions/index.ts 是派发中枢:OPENWORK_EXPERIMENTAL_EXTENSION_ACTIONS 汇总所有内建扩展的 action;callExperimentalExtensionAction(extensions/index.ts:37)按 extensionId 找到对应模块调用;找不到实现则回 501「已注册但未实现」。HTTP 入口在 routes/core.ts:266(list)与 :275(call)。
两个现成扩展:
扩展(extensionId) | 提供的 action(节选) | 鉴权 |
|---|---|---|
google-workspace | calendar_list_events、gmail_create_draft、drive_search_files、chat_send_message… | Google OAuth |
openai-image-generation | status、image_generate(gpt-image-2 出 PNG) | OpenAI API key |
生图扩展的落地细节值得一看(openai-image-generation.ts:154 的 generateOpenAiImageArtifact):key 依次从环境变量服务和进程环境找(OPENWORK_OPENAI_IMAGE_API_KEY → OPENAI_API_KEY,:55);生成的 PNG 只写到工作区的 artifacts/ 下,且过 resolveSafeChildPath 防路径穿越(:101);把 base64 解码落盘后回一个相对路径(:164-178)。
4. 巧妙之处(可借鉴的技术)
-
一份安装逻辑,喂三种来源。 云插件、Claude 插件仓库最终都被规整成
CloudPluginResolved,复用同一个installCloudPlugin(cloud-plugins.ts:596)。新增一种「插件源」= 写一个解析器,而非再抄一套安装/卸载/命名空间/回滚。这是「归一化到一个内部表示」的典范。 -
用户配置与工具配置分家。 UI 加的 MCP/插件进 runtime DB,不污染用户手写的
opencode.json;构建时才合并注入(openwork-runtime-config.ts:94)。用户随时能把自己的配置提交进 git,而不会混入 OpenWork 的私货。 -
单条失败不连坐 + 有限重试。 引擎 MCP 热同步逐条推、坏条目跳过、5xx 才重试一次(
server.ts:2851、postMcpEntryWithRetry:2883)。一个失效的第三方 MCP 不会让整批注册瘫痪。 -
降级要吵、要留痕。 Claude 插件遇到不支持的
hooks/${CLAUDE_PLUGIN_ROOT}/多余文件,一律进warnings而非静默丢弃(claude-plugin-bundle.ts:291);MCP 同步失败也记录并经engineSync字段回给 UI(server.ts:2144)。宁可让用户看见「装了但有东西没生效」。 -
元工具做能力发现。 内建扩展不塞满工具列表,而是
list_actions+call两个元工具按需发现(openwork-extensions-preview.ts:209),配一句系统提示引导。加新扩展不用改 agent 的工具 schema。
5. 边界与局限(诚实)
- Claude 插件的 hooks 不支持。 声明了 hooks 会被跳过并告警(
claude-plugin-bundle.ts:291)。 - 插件本地 MCP 命令不支持。 用
${CLAUDE_PLUGIN_ROOT}指向插件内命令的 MCP 会被跳过(claude-plugin-bundle.ts:344)——OpenWork 只接远程/独立 MCP。 - 技能只装
SKILL.md(部分路径)。 Claude 插件里带额外文件的技能,通过 bundle 路径导入时只装SKILL.md(claude-plugin-bundle.ts:331);但 §3.1 的 Hub 安装路径会装全整个技能目录——两条路径行为不同,注意区分。 - 内建扩展是实验性的。 走的是
/experimental/extensions/*,注册了但没实现的 action 会明确回 501(extensions/index.ts:63)。目前只有 Google Workspace 和 OpenAI 生图两个。 - 只接受 github.com 的插件源。 非 github.com 的 URL 直接拒(
claude-plugin-bundle.ts:77)。
6. 横向对比
本章讲的是 OpenWork「装能力」这一面;它和同组其它章共同拼出完整系统:
- 承载这些读写 API、鉴权、审计的服 务端本身 → 03-openwork-server。
- 引擎、服务端、路由三个 sidecar 由谁监管、如何启停 → 02-orchestrator。
- 这些扩展在 UI 里长什么样、会话如何实时渲染工具调用 → 05-frontend。
- 远程/云工作区下 MCP 的 loopback 兜底与托管 worker → 06-remote-cloud。
与 shelf 内其它 coding agent 的取舍:OpenWork 不自造扩展协议,而是站在 OpenCode 的原生扩展点上(技能/插件/MCP 都是 OpenCode 的概念),只在其上加「一键安装 + 托管扩展 + UI」。它的差异化不在「发明新扩展模型」,而在「把已有扩展模型的安装体验做到点几下」。
7. 代码地图(导航索引)
| 主题 | 文件 | 关键符号 |
|---|---|---|
| 技能扫描(多来源/多布局) | apps/server/src/skills.ts | listSkills、listSkillsInDir、parseSkillEntry |
| 技能写入 | apps/server/src/skills.ts | upsertSkill、buildSkillContent、deleteSkill |
| 技能远程安装 | apps/server/src/skill-hub.ts | listHubSkills、installHubSkill、DEFAULT_HUB_REPO、resolveSafeChild |
| MCP 列举/增删/开关 | apps/server/src/mcp.ts | listMcp、addMcp、removeMcp、setMcpEnabled、isMcpDisabledByTools |
| MCP 引擎热同步 | apps/server/src/server.ts | syncRuntimeMcpToOpencodeEngine、postMcpEntryWithRetry、syncAllWorkspacesRuntimeMcpToEngine |
| 插件列举/增删/归一化 | apps/server/src/plugins.ts | listPlugins、addPlugin、normalizePluginSpec |
| 云插件安装/卸载 | apps/server/src/cloud-plugins.ts | installCloudPlugin、removeCloudPlugin、getPluginObjectInstallPath |
| Claude 插件包解析 | apps/server/src/claude-plugin-bundle.ts | resolveClaudePluginBundle、parseClaudePluginSource、locatePluginRoot |
| 内建扩展派发 | apps/server/src/extensions/index.ts | callExperimentalExtensionAction、listExperimentalExtensionActions |
| Google Workspace 扩展 | apps/server/src/extensions/google-workspace.ts | GOOGLE_WORKSPACE_EXTENSION_ACTIONS、callGoogleWorkspaceExtensionAction |
| OpenAI 生图扩展 | apps/server/src/extensions/openai-image-generation.ts | OPENAI_IMAGE_GENERATION_EXTENSION_ACTIONS、generateOpenAiImageArtifact |
| 引擎内元工具桥 | apps/server/src/opencode-plugins/openwork-extensions-preview.ts | OpenWorkExtensionsPreview、openwork_extension_call |
| 内建插件路径解析 | apps/server/src/openwork-extensions-plugin-path.ts | openworkPluginPath、openworkExtensionsPreviewPluginPath |
| 最终引擎配置构建 | apps/server/src/openwork-runtime-config.ts | buildOpenworkRuntimeConfigObject、注入 plugin 数组 + runtimeMcpMap |
| runtime 配置存取 | apps/server/src/runtime-opencode-config-store.ts | runtimeMcpMap、runtimePluginList、writeRuntimeOpencodeConfig |
| 路径约定 | apps/server/src/workspace-files.ts | projectSkillsDir、projectPluginsDir、opencodeConfigPath |
| 前端内建工具类型 | apps/app/src/lib/build-in-tools.ts | SkillToolPart、isSkillToolPart 等识别函数 |
| 扩展设置面板 | apps/app/src/react-app/domains/settings/pages/extensions-view.tsx | ExtensionsView、ExtensionsSection |
| MCP OAuth 模态 | apps/app/src/react-app/domains/connections/mcp-auth-modal.tsx | OAuth 授权 URL / callback 处理 |