自定义插件
编写、发布并配置可安装的 Downcity Plugin
自定义插件
一个第三方目录只定义一个 Plugin。Plugin 的全局唯一 ID 同时是最终目录名、Agent 引用键和运行时 Registry key。
开发目录
github-plugin/
├── plugin.json
├── package.json
├── README.md
├── icon.svg
├── src/
│ ├── plugin.ts
│ └── setup.ts
├── tsconfig.json
└── dist/setup.jssrc/ 和 tsconfig.json 只服务本地开发。Downcity 不规定构建工具,也不需要 tsup.config.ts。package.json 属于 Plugin 包,用来声明明确的 ESM package 边界,因此必须包含 "type": "module" 并会随 Plugin 安装。构建脚本只需把入口及其运行时依赖打包成一个自包含 ESM 文件;例如可直接在 package.json 中调用 esbuild:
{
"type": "module",
"scripts": {
"build": "esbuild src/setup.ts --bundle --platform=node --format=esm --outfile=dist/setup.js"
}
}SDK Plugin Class 不受 CLI 配置协议限制。构造参数完全由 Plugin 自己决定;需要 Provider、Client 或本地适配器时,直接在 SDK 中传入:
import type { Plugin, PluginActions } from "@downcity/agent";
export interface GithubPluginConfig {
api_url: string;
token: string;
}
export class GithubPlugin implements Plugin {
readonly name = "github";
readonly title = "GitHub";
readonly description = "GitHub 集成";
readonly actions: PluginActions;
constructor(config: GithubPluginConfig) {
this.actions = {};
}
}SDK 用户直接装配实例,Agent API 保持简单:
const plugin = new GithubPlugin({
api_url: "https://api.github.com",
token: process.env.GITHUB_TOKEN ?? "",
});
const agent = new Agent({
id: "coding-pro",
model,
plugins: [plugin],
});实例 name、plugin.json 的 id、目录名和 Agent 引用必须一致。SDK 不要求 Plugin 导出固定 constructor,也不要求 constructor 接收 profile。
Plugin 定义
源目录只保留一个 plugin.json:
{
"schema_version": 1,
"id": "github",
"version": "1.0.0",
"title": "GitHub",
"description": "GitHub 集成",
"icon": "./assets/github.svg",
"setup": "dist/setup.js"
}setup 指向一个自包含 ESM 模块。该模块导出配置 schema 和宿主装配函数:
import type { PluginHostContext } from "@downcity/agent";
export const schema = {
type: "object",
properties: {
api_url: { type: "string", format: "uri" },
token: { type: "string", minLength: 1, writeOnly: true },
},
required: ["api_url", "token"],
additionalProperties: false,
} as const;
export function setup(context: PluginHostContext): GithubPlugin {
return new GithubPlugin(context.profile as GithubPluginConfig);
}setup 是 City 的装配边界,不是安装脚本,也不是 Plugin 生命周期。每次调用都必须创建新的 Plugin 实例;context.embassy、context.data_path 和 context.extensions 只代表当前宿主提供的能力。context.data_path 是运行时私有目录,不用于保存 profile 配置;profile 由 City 在 ~/.downcity/plugins/<plugin_id>/config.toml 中统一管理,多个 Agent 可以引用同一个 profile。需要登录态的 Plugin 应显式检查 context.embassy 是否存在。
icon 可选,支持 http://、https:// 远程地址,或 Plugin 根目录内的相对路径。相对路径对应的文件会随安装复制,并且不能使用 symlink 或路径逃逸。
README.md 是第三方 Plugin 的必需文件,安装后会保留在 Plugin 目录,供 CLI、Desktop 和用户离线查看。
配置 schema 使用 JSON Schema 2020-12。常用字段包括:
type、properties、required、additionalPropertiesenum、const、oneOf、anyOf、allOf- 字符串的
minLength、maxLength、pattern、format - 数值的
minimum、maximum、multipleOf - 数组的
items、minItems、maxItems、uniqueItems default、examples、title、description- 敏感字段使用
writeOnly: true
例如:
{
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"format": "uri"
},
"mode": {
"type": "string",
"enum": ["safe", "fast"],
"default": "safe"
},
"token": {
"type": "string",
"minLength": 1,
"writeOnly": true
}
},
"required": ["endpoint", "token"],
"additionalProperties": false
}表单展示所需的占位符、排序或控件提示放在 x_downcity 下,不改变 JSON Schema 的运行时校验语义。
字段级 default 只作为 JSON Schema 注解,供新 Profile 表单初始化,不会自动写入未选择 Profile 的运行时配置。Schema 的唯一事实源是 setup 模块;plugin.json 不重复保存 config、schema 或 defaults。
setup 必须指向单个自包含 ESM 文件。安装器不会执行依赖安装、构建脚本或调用 setup(),也不会复制入口之外的源码和开发文件。Plugin 使用 Zod 等运行时依赖时,构建工具必须把依赖打进入口。
安装后的目录是确定的:
~/.downcity/plugins/github/
├── plugin.json
├── config.toml
├── package.json
├── README.md
├── assets/github.svg
└── dist/setup.jsconfig.toml 只保存命名的明文 profile 值,文件权限为 0600。它属于 Plugin 的全局配置,不复制到 Agent 目录。CLI 与 Desktop 在需要配置或装配时加载 setup 模块的 schema,构建表单并脱敏 writeOnly 字段。普通安装和 Plugin 列表不会调用 setup()。Plugin 的运行时状态和缓存使用 context.data_path,CLI 与 Desktop 按 Agent 和 Plugin 隔离,不因进入不同 Workspace 而复制。
schema_version = 1
[profiles.production]
api_url = "https://api.github.com"
token = "plain-local-token"Agent 的 Plugin 引用有两种运行语义:没有显式 profile 时直接使用空对象 {},永远不会隐式读取名为 default 的 Profile;显式选择 profile 时读取该全局 Profile,不存在则报错。两种配置都会执行 setup 模块 Schema 校验。Schema 接受 {} 的 Plugin 可以直接启用,Schema 要求字段的 Plugin 必须先创建并显式选择 Profile。
安装与配置
city plugin install ./github-plugin
city plugin config github production --interactive
city plugin enable github my-agent --profile production
city plugin update github
city plugin uninstall github更新会原子替换整个 Plugin 目录并保留 config.toml。新 Schema 无法校验已有 profile 时更新失败;仍被 Agent 引用的 profile 或 Plugin 不能删除。
安装过程只读取并复制 plugin.json、package.json 与声明的 setup,不会执行 npm install、构建脚本、Plugin lifecycle 或调用 setup()。City 在配置或 Agent 装配时读取 schema 并验证 setup 导出;Agent 真正装配时再调用 setup(context) 并验证实例 ID。package.json 与 setup 会生成 SHA-256 摘要。只应安装可信来源。
继续阅读: