Plugin

自定义插件

编写、发布并为 Agent 配置可安装的 Downcity Plugin

自定义插件

一个 CLI 入口可以导出多个 Plugin constructor。每个 Plugin 自己持有静态 Manifest 和可选 Resource Resolver;CLI 负责安装、校验并统一实例化。

制品结构

example-connectors/
├── downcity.plugin.json
└── dist/
    └── index.js

入口只需要导出数组:

export const plugins = [GithubPlugin, LinearPlugin];

不需要 PluginProject 或每个 Plugin 的 Factory。

Plugin constructor

import type { JsonObject } from "@downcity/agent";

export class GithubPlugin {
  static readonly manifest = {
    name: "github",
    version: "1.0.0",
    title: "GitHub",
    description: "GitHub 集成",
    config: {
      schema: GITHUB_CONFIG_JSON_SCHEMA,
      defaults: {
        api_url: "https://api.github.com",
      },
    },
    resources: {
      schema: GITHUB_RESOURCE_JSON_SCHEMA,
    },
  };

  static async resolve_resource({ resource }: { resource: JsonObject }) {
    const user = await get_github_user(String(resource.token));
    return { name: user.name, login: user.login };
  }

  readonly name = "github";
  readonly actions = {};

  constructor({ config, resources }: {
    config: JsonObject;
    resources: JsonObject[];
  }) {
    // config 和 resources 已经由 CLI 校验并解析完成。
  }
}

CLI 最终统一执行:

new GithubPlugin({ config, resources });

实例 name 必须与 static manifest.name 一致。

Config 与 Resource Schema

Schema 使用 JSON Schema 2020-12,并直接放在 Plugin 静态 Manifest 中。可以使用 Zod 作为源码定义:

import { z } from "zod";

const config_schema = z.object({
  api_url: z.url(),
}).strict();

export const GITHUB_CONFIG_JSON_SCHEMA = z.toJSONSchema(config_schema, {
  target: "draft-2020-12",
});

Resource Schema 描述完整 Item,要求 idtypenameid 由 City 写入;普通字段和 writeOnly 字段由用户填写;其他 readOnly 字段由 static resolve_resource 返回。

每个 Plugin 必须提供非空 description。City 会在普通 Plugin 列表、交互式列表和 JSON Catalog 中统一展示这段用途说明。

Resolver 只在 Resource 创建、编辑或刷新时调用。Agent 启动只读取已经保存的完整 Resource Item,不会隐式联网刷新。

静态安装清单

安装阶段不会执行第三方入口,因此构建过程必须从 Plugin 的静态 Manifest 生成并提交 downcity.plugin.json

{
  "manifest_version": 3,
  "entry": "dist/index.js",
  "plugins": [
    {
      "name": "github",
      "version": "1.0.0",
      "title": "GitHub",
      "config": {
        "schema": {
          "type": "object",
          "properties": {
            "api_url": { "type": "string", "format": "uri" }
          },
          "additionalProperties": false
        },
        "defaults": {
          "api_url": "https://api.github.com"
        }
      }
    },
    {
      "name": "linear",
      "version": "1.0.0",
      "title": "Linear",
      "description": "Linear 集成"
    }
  ]
}

运行时,CLI 会确认 plugins[] 中每个 constructor 的静态 Manifest 与该安装快照一致。

Action 不写入静态 Manifest。实际 Action 只由 Plugin 实例的 actions 定义,避免维护重复的 Action 名称列表。

安装与配置

city plugin install ./example-connectors
city plugin install github:acme/example-connectors#main

city plugin resource create github --interactive
city plugin config github my-agent --interactive
city plugin enable github my-agent

city plugin update github
city plugin uninstall github

如果一个入口同时导出 githublinear,更新其中任意一个都会更新整个共享入口;卸载其中任意一个会卸载该入口的全部 Plugin。存在 Binding 或 Resource 时 CLI 会拒绝卸载。

安全边界

安装过程只读取静态 Manifest 和制品文件,不执行 npm install、构建脚本、生命周期脚本或 Plugin 入口。City 会记录 Git commit 和完整制品的 SHA-256 摘要。

Plugin 入口会在创建或刷新 Resource,以及 Agent 启动时执行,因此只应安装可信来源。

继续阅读: