首页
/ 构建可浏览的 Corsair 插件目录:corsair-explorer 独立服务全解析

构建可浏览的 Corsair 插件目录:corsair-explorer 独立服务全解析

2026-09-14 17:15:35作者:盛欣凯Ernestine

corsair-explorer 是 Corsair 仓库中一个独立、可独立部署的 Express 服务,它把仓库内所有 @corsair-dev/* 插件及其定义的 api、webhook、db 三类操作,静态化为一份公开、可浏览、可检索的 JSON 目录(catalog)。本文将从构建管线、目录数据结构、REST API、运行配置到源码实现逐层展开,读完你将掌握如何重新生成目录、本地运行/生产部署这个服务,并通过 REST 或编程接口把插件能力开放给任何客户端。

一、为什么需要 explorer:把"未安装的插件"也变成可发现资产

在 Corsair 的生态里,插件(plugin)是连接用户第三方应用(如 Slack、Gmail、GitHub)能力的核心单元,每个插件会声明若干 api(可调用的端点)、webhook(可订阅的事件)与 db(可同步的实体)。普通用户只有在本地安装某个插件后,才能看到它内部暴露了哪些能力——这显然限制了营销站、文档站对插件生态的展示。

explorer/README.md 给出的解法是:用一个小型独立 Express 服务器,公开一个可浏览的全量插件目录,让用户在没有本地安装任何插件的情况下,也能探索所有插件及其全部操作。它被设计为部署在稳定的 URL(例如 api.corsair.dev)之后,供营销站点直接指向,从而让"浏览插件"这件事与本地运行时彻底解耦。

从目录结构上看,explorer/ 拥有自己的 pnpm-workspace.yaml(空文件)、package.json 与 lockfile,不属于 monorepo workspace 包,因此可以独立容器化或移动到任何位置,不会拖带上仓库其余部分。它与 monorepo 的唯一连接点,是负责产出 data/catalog.jsondata/plugins/*.json 的生成脚本。

二、工作原理:从插件源码到静态 JSON 的两段式管线

整个 explorer 的运作可以拆成"构建期"与"运行期"两个完全解耦的阶段:

[构建期]  scripts/build-explorer-catalog.ts
         ├─ 扫描 packages/* 下每个 @corsair-dev/* 插件
         ├─ 动态 import 插件工厂,执行 introspectPluginForDocs()
         └─ 写出 explorer/data/catalog.json + explorer/data/plugins/<id>.json

[运行期]  src/server.ts(Express)
         ├─ 启动时读取 catalog.json 索引
         ├─ 按需懒加载 data/plugins/<id>.json
         └─ 通过 REST /v1/* 对外提供 JSON

2.1 构建期:introspectPluginForDocs 的静态化

scripts/build-explorer-catalog.ts 是整条管线的入口。它会遍历 packages/* 下的所有插件包(跳过 corsairclimcpuistudio 等非集成插件目录),动态导入每个插件工厂,调用 Corsair 核心的 introspectPluginForDocs(定义在 packages/corsair/core/inspect/index.ts)对其做文档级自省,然后把结果序列化为两类产物:

  • explorer/data/catalog.json —— 目录元数据、插件摘要列表(summary)与一份扁平化的搜索索引;
  • explorer/data/plugins/<id>.json —— 每个插件的完整记录(含全部 api / webhook / db 详情)。

值得注意的是,脚本头部注释明确说明它刻意维护了一份"小而自包含"的发现逻辑副本(与 scripts/generate-plugin-docs.ts 同源但可独立演进),插件的展示文案(displayNamedescription)优先读取 packages/<plugin>/plugin-docs.yaml,缺失时回退到各包的 package.json

2.2 运行期:只依赖 JSON,不依赖 corsair

运行期服务 explorer/src/server.ts 启动时只做一件事:读取 catalog.json 索引,之后对 data/plugins/*.json 按请求懒加载。这意味着 explorer 对 corsair 核心没有任何运行时依赖——它只是一个读取 JSON 的静态数据服务,天然轻量、易部署、易缓存。

2.3 重新生成目录

每当插件有增删改,从仓库根目录执行:

pnpm build:explorer-catalog

生成的 explorer/data/catalog.jsonexplorer/data/plugins/ 应随部署一并提交(commit)。在 package.json 中也能看到这一脚本(build:explorer-catalog)与 explorer 目录的数据文件是配套提交的。

三、目录数据结构:index + 每插件文件,并兼容 v1 单体格式

3.1 catalogVersion 2(当前格式)

explorer/src/types.ts 明确定义了 PluginCatalogIndex(catalogVersion 恒为 2)的骨架:

字段 类型 说明
generatedAt string 目录构建时间的 ISO 时间戳
corsairVersion string 构建时 corsair 包版本
catalogVersion 2 供消费方做破坏性变更防护的 schema 版本
plugins PluginSummary[] 每插件一行摘要(不含操作详情)
search CatalogSearchEntry[] 扁平化搜索索引

从实际的 explorer/data/catalog.json 可以看到真实数据形态,例如第一个插件:

{
  "generatedAt": "2026-09-08T17:15:47.532Z",
  "corsairVersion": "0.1.128",
  "catalogVersion": 2,
  "plugins": [
    {
      "id": "ably",
      "displayName": "Ably",
      "description": "Realtime messaging platform for pub/sub, chat, and live data streaming at scale.",
      "npmPackageName": "@corsair-dev/ably",
      "authTypes": ["api_key"],
      "defaultAuthType": "api_key",
      "counts": { "api": 26, "webhooks": 0, "db": 0 }
    }
  ]
}

3.2 PluginEntry:插件的完整记录

每个 data/plugins/<id>.json 对应一个 PluginEntry,它在 PluginSummary 基础上追加四个数组:

  • auth: PluginAuthFields[] —— 每种受支持认证类型的字段元数据,按 oauth_2 | api_key | bot_token 三种认证类型组织,并区分存储在集成层(shared/provider)的 integrationFields 与账户层(per-tenant)的 accountFields
  • api: DocsApiEndpoint[] —— 每个端点含 pathshortPath(如 messages.post)、可选 descriptionriskLevelread | write | destructive)、irreversibleinput/output 的 schema 描述;
  • webhooks: DocsWebhook[] —— 含 pathshortPathpayload schema、responseTypeusageExample
  • db: DocsDbEntity[] —— 可同步实体,含 entityName 与可过滤字段 filters(每个字段声明 string | number | boolean | date 类型及其可用操作符)。

3.3 兼容 v1 单体格式

explorer/src/catalog.tsloadCatalog 在读取文件后会做形状判定:若是 catalogVersion === 2 的 index,则进入"索引 + 按插件文件懒加载"模式;若是 catalogVersion === 1 的遗留单体 PluginCatalog(所有插件塞在一个文件里),则通过 catalogFromLegacyMonolith 自动转换成 index 形态并预载全部插件,向上兼容老数据。resolveCatalogPath 还会在目标路径不存在时回退尝试同目录下的 plugins.json,进一步兼容旧布局。

四、安装、运行与生产部署

4.1 一次性安装(在 explorer 目录内)

cd explorer
pnpm install

这会生成 explorer/node_modulesexplorer/pnpm-lock.yaml,与 monorepo 完全隔离。服务仅有一个运行时依赖 express ^4.21.0(见 explorer/package.json),开发期使用 tsx 直接跑 TypeScript。

4.2 本地开发

pnpm dev
# → http://localhost:4319

dev 脚本是 tsx watch src/cli.ts,支持热重载。启动成功后,explorer/src/cli.ts 会打印一行摘要,例如:

[corsair:explorer] listening on http://0.0.0.0:4319 — 285 plugins, generated 2026-09-08T17:15:47.532Z

(实际插件数量以你本地生成的 catalog.json 为准。)

4.3 环境变量

变量 默认值 用途
PORT 4319 绑定端口
HOST 0.0.0.0 绑定主机
EXPLORER_CATALOG_PATH 打包内置的 data/catalog.json 覆盖目录索引位置
EXPLORER_CORS_ORIGIN * CORS 允许的 origin 头

cli.ts 中的解析逻辑为 Number.parseInt(process.env.PORT ?? '', 10) || 4319,即非法或缺失的端口值都会安全回退到默认端口。EXPLORER_CORS_ORIGIN 默认不设置,交由 createServer 的默认值 * 兜底。

4.4 生产构建与启动

pnpm build
pnpm start

build 脚本是 tsup && tsc -p tsconfig.build.json,产出 dist/startnode dist/cli.jsexplorer/package.json 同时声明了 bin 字段(corsair-explorer),并保证发布产物包含 distdataicons 三个目录。

五、REST API 全览

所有路由均返回 JSON 且默认美化输出(app.set('json spaces', 2),见 explorer/src/server.ts)。完整路由表如下:

路由 说明
GET / 服务自描述:名称、描述与全部端点列表
GET /health 存活探针,返回 { ok: true }
GET /v1/meta 目录元数据:generatedAtcorsairVersioncatalogVersionpluginCount
GET /v1/plugins 摘要列表(不含操作详情)
GET /v1/plugins/:id 插件完整记录
GET /v1/plugins/:id/api 某插件全部 API 端点
GET /v1/plugins/:id/api/:shortPath 按 shortPath(如 messages.post)取单个端点
GET /v1/plugins/:id/webhooks 某插件全部 webhook
GET /v1/plugins/:id/webhooks/:shortPath 按 shortPath 取单个 webhook
GET /v1/plugins/:id/db 某插件全部同步 db 实体
GET /v1/plugins/:id/db/:entity 按实体名取单个实体
GET /v1/search?q=<query> 跨插件 id、显示名、描述与端点 shortPath 的子串匹配搜索

5.1 统一响应头与缓存失效

中间件为每个响应统一附加两个头(explorer/src/server.ts):

  • X-Catalog-Generated-At —— 目录生成时间;
  • X-Corsair-Version —— 构建目录时的 corsair 版本。

客户端可以据此判断目录是否已重建,从而主动失效本地缓存。

5.2 错误语义与通配符细节

  • 插件不存在统一返回 404,错误体形如 { error: 'plugin_not_found', message: 'No plugin "xxx" in the catalog.' }
  • 端点/webhook/实体不存在时,404 响应体不仅给出 errormessage,还会附带 available 字段列出该插件实际可用的 shortPath/实体名,方便客户端自纠错;
  • 单个端点路由使用 Express 通配符 *(而非 :shortPath)实现,正是为了保住 messages.post 这类带点的 shortPath;源码中的 getWildcardParam 会从 req.params<a href="https://link.gitcode.com/i/93c39410840317dc2e48650a4a30257e" target="_blank">'0'] 取出通配符匹配值([explorer/src/server.ts);
  • 未带 q 参数的搜索请求返回 400missing_query)。

5.3 搜索接口

/v1/search?q=<query> 返回 { query, results },其中 results 分三组:命中的插件摘要、命中的 api 端点(带 pluginId)、命中的 webhook(带 pluginId)。搜索是大小写不敏感的子串匹配,且索引在构建期就已生成好:buildSearchIndex 为每个 api 端点拼接 shortPath + path + description + riskLevelhaystack,为每个 webhook 拼接 shortPath + path + description,全部小写化后写入 catalog.jsonsearch 数组(explorer/src/catalog.ts)。运行期只需遍历索引做 includes 判断,命中后再按需加载对应插件文件补齐详情,因此搜索响应非常快。

六、编程式集成:作为库嵌入你的服务

除了独立 CLI 运行,explorer 还以库的形式导出,可在任意 Express 应用中直接挂载(见 explorer/README.md 的 Programmatic use 一节,导出实现在 explorer/src/index.ts):

import { createServer, loadCatalog } from 'corsair-explorer';

const catalog = loadCatalog();
const app = createServer({ catalog });
app.listen(4319);

createServer 接受 { catalog, corsOrigin? } 两个选项(explorer/src/server.ts),其中 corsOrigin 默认 *——源码注释明确说明原因是目录只含公开的插件元数据、不含任何密钥,因此允许任何浏览器客户端访问是安全的。这一设计让 explorer 既能独立部署,也能被测试或与其他中间件组合挂载。

七、源码级原理:懒加载、缓存与数据校验

explorer/src/catalog.ts 中的 Catalog 类是运行期的核心,值得关注的实现细节有三点:

  1. 按需懒加载 + 内存缓存loadPlugin(id) 先查 pluginCacheMap<string, PluginEntry>),未命中再读 data/plugins/<id>.json 并解析、校验、缓存(explorer/src/catalog.ts)。对于 v1 遗留单体数据,则通过 preloadedPlugins 一次性预载。
  2. 严格形状校验:加载的插件文件必须通过 isPluginEntry 校验(id、displayName、npmPackageName 为字符串,auth/api/webhooks/db 为数组,counts 为对象),解析失败或形状不符都会抛出带 [corsair:explorer] 前缀、含文件路径的明确错误;索引读取失败时错误信息会提示 Did you run pnpm build:explorer-catalog?,引导用户先构建数据。
  3. 摘要与详情的分层listSummaries() 直接返回索引中的 PluginSummary[](只含 id、displayName、description、npmPackageName、authTypes、defaultAuthType、counts),列表接口不触发任何插件文件加载;只有 getPlugin/findApiEndpoint/findWebhook/findDbEntity 这类需要详情的调用才触发懒加载。分层带来的直接收益是 /v1/plugins/v1/meta 即使插件数量再多也只需一次索引读盘。

八、把 explorer 放进你的部署

综合以上,一套完整的落地路径是:

  1. 在仓库根目录执行 pnpm build:explorer-catalog,把生成的 explorer/data/catalog.jsonexplorer/data/plugins/ 提交入库;
  2. explorer/ 内执行 pnpm install(独立 lockfile,不污染 monorepo);
  3. 生产环境执行 pnpm build && pnpm start,用 PORT/HOST 控制监听地址,EXPLORER_CORS_ORIGIN 收敛跨域来源;
  4. 把它放在稳定 URL(如 api.corsair.dev)之后,让营销站点/文档站通过 /v1/* 展示未安装插件的全部 api、webhook 与 db 能力;
  5. 利用 X-Catalog-Generated-At 响应头做客户端缓存失效,插件更新后重新跑一次生成脚本即可。

由于运行期完全不依赖 corsair 核心,只消费静态 JSON,explorer 可以放心地容器化、水平扩展甚至迁移到 CDN 之后——它本质上是把"插件生态"变成了一份任何客户端都能消费的公开数据面。

延伸阅读:插件文档生成主脚本见 scripts/generate-plugin-docs.ts;文档自省能力定义在 packages/corsair/core/inspect/index.ts;插件目录数据实样见 explorer/data/catalog.json

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.81 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
945
1.86 K
docsdocs
暂无描述
Markdown
906
5.84 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
537
607
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.28 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.39 K
1.48 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
401
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.19 K
347