• 简体中文
  • 插件开发与排障

    本页供接入业务工具和排查运行问题时查阅。创建第一个包先读 Quickstart;本文中的 provider 指提供业务工具的本地进程,profile 指设备上保存的命名配置。

    本地环境

    从同一个 checkout 启动常规服务栈与 Desktop/Runner:

    首次使用先从 docker/.env.example 创建私有的 docker/.env.dev,按仓库开发环境说明配置模型网关等服务。示例配置中的模型凭据为空,直接启动 API 会失败。Dev 插件签名准备也需要这个文件。

    just dev
    just desktop-dev

    本地 origin 是 https://liyanlabs.local。不要同时从两个 worktree 运行完整 Compose 栈;stack、数据库、Redis、端口和 Desktop 连接都是共享资源。

    just dev 会在服务启动前准备 Dev 插件签名与信任配置。desktop/scripts/provision-dev-plugin-runner.mjs 生成或复用以下忽略提交的本地材料:

    • Dev 服务端 Ed25519 signing key;
    • 服务端加密 keyring(不用于保存 device-local 操作凭据);
    • Dev channel home 下的公开 trust resource。

    just desktop-dev 生成并暂存 runtime manifest、填充本地 runtime cache、构建 Runner、启动 Electron。API、Web、Worker 在 just dev 下热更新,普通源码修改不需要重建整套栈。

    排查插件前确认:

    1. Desktop 显示 channel dev、backend origin https://liyanlabs.local。
    2. Trust resource 指向精确 WSS origin 和 /liyanlabs/ws/v1/runner/device 路径。
    3. Desktop enrollment 是 active,lease expiry 持续推进,并存在 catalog digest。
    4. Installation 与指定 device 属于同一个认证用户。

    配置本地连接

    本地 provider 的配置与包启用独立。首次安装保存启用意图;更新或修复后需重新启用。

    1. 选择在线 Desktop 并安装插件,等待包确认完成。
    2. 打开配置入口。带 setup Skill 的插件可选 AI 帮助,也可手动配置或稍后配置;新 CLI 模板已包含 setup / usage Skills。
    3. 创建命名本地 profile。Label trim 后为 1–128 个 Unicode code point,允许字母、数字、空格、.、_、-。
    4. 在绑定 Desktop 的原生窗口完成配置。飞书优先扫码注册;“使用已有应用”提供区域、App ID 与 App Secret 输入。GitHub 使用其设备本地 token 配置。
    5. 审核业务权限。空配置也须保存;普通本地工具不需要外部账号或入站 Agent。飞书入站消息默认关闭,需要时单独同意并绑定 Agent。
    6. 通过 setup reconcile 刷新配置状态,再查看设备和 provider 是否健康。

    操作凭据只进入设备安全存储,不粘贴到聊天、manifest、普通 Web / Mobile 表单或日志。保存配置只证明本地存储成功;连接健康仍需认证探测。AI plugin_setup 的 status 是读取,reconcile 是维护动作,继续遵守当前会话权限模式。

    插件包契约

    作者清单是根目录 plugin.json,使用 Agent Plugins 1.0.0;宿主扩展使用 extensions["com.liyanlabs"] 和 extension 2.0.0。工具实现、资源和入口随插件一起发布。可复制的完整示例见Quickstart。

    {
      "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
      "name": "com.liyanlabs.example-tools",
      "version": "1.0.0",
      "author": { "name": "Example Developer" },
      "extensions": {
        "com.liyanlabs": {
          "schema": "https://liyanlabs.com/schemas/plugin-extension/2.0.0",
          "displayName": "示例工具",
          "icon": "assets/icon.png",
          "localProviders": [
            {
              "id": "tools",
              "provider": "example",
              "protocol": "plugin-provider/1",
              "transport": "stdio",
              "entrypoint": {
                "path": "bin/provider",
                "args": [],
                "workingDirectory": "."
              },
              "connectionModes": ["device_local"],
              "toolCatalog": "tools.json",
              "configuration": "configuration.json"
            }
          ]
        }
      }
    }

    入口和资源都使用包内安全相对路径。id 是 capability 标识,provider 是配置和协议标签;一个包内各标签唯一,跨安装允许重复。它们不代替完整安装身份。普通 provider 不需要在宿主登记名字。

    tools.json 使用 liyanlabs.plugin-tools/1:每项包含 name、description、inputSchema 和显式 readOnly。输入 schema 离线编译,禁止外部资源获取。configuration.json 使用 liyanlabs.plugin-configuration/1:title、fields 和可选 registration;字段类型为 text / password / select,并声明长度、是否必填、选项和默认值。没有凭据需求时可声明空字段列表。

    注册入口同样是包内程序。它输出逐行 verification(URL、expiresIn)、configured(values)或闭合 error 帧;URL 只能来自声明的 HTTPS origins。Desktop 仅向普通界面展示安全验证链接,主进程校验并保存结果。取消、退出和设备撤销会清理整个进程树。

    Skill 位于 skills/<key>/SKILL.md 并显式声明 setup / usage;MCP HTTP 声明、其他 Channel 和 Runtime 的限制见能力类型。验证不执行代码,不获取远程 schema;拒绝未知字段、重复 JSON key、链接、特殊文件和不规范路径。最多 2,000 个文件;非程序资源总量不超过 64 MiB,运行包总量不超过 512 MiB。普通第三方程序需用户明确同意本地执行权限。

    工具输入 Schema 支持 JSON Schema draft-07(http://json-schema.org/draft-07/schema#)和 2020-12(https://json-schema.org/draft/2020-12/schema)。省略 $schema 时使用 2020-12。命令行和导入均离线校验,引用必须留在当前文档内。

    工具协议与运行产物

    区分设备桥与插件进程:plugin-provider/1 和 plugin_provider.tools.list / call 是宿主与 Runner 的受认证请求;作者的 stdio 程序接收 MCP JSON-RPC 2.0 方法 initialize、notifications/initialized、tools/list、tools/call,不解析设备桥 envelope。

    Runner 先验证可执行文件与完整安装绑定,再通过私有继承 FD / Windows handle 提供一次性 plugin-provider-bootstrap/2。定位变量是 LIYANLABS_PROVIDER_BOOTSTRAP_FD;其中含精确 binding、配置 schema 摘要、配置版本、私有配置值和本地数据目录。读取后关闭并清除定位变量。凭据不进入 argv、普通环境变量、工具参数或 stdout。飞书代码演示该读取边界。

    程序 stdout 只输出有界逐行 MCP JSON。请求示例:

    {
      "jsonrpc": "2.0",
      "id": "actual-request-id",
      "method": "tools/call",
      "params": { "name": "team.botInfo", "arguments": {} }
    }

    对应结果:

    {
      "jsonrpc": "2.0",
      "id": "actual-request-id",
      "result": {
        "content": [{ "type": "text", "text": "实际返回的机器人信息" }],
        "isError": false
      }
    }

    发现结果的 annotations.readOnlyHint 必须为显式布尔值并与资源分类一致。Runner 匹配整份工具目录的数量、唯一名字、schema 和分类,再允许调用;禁止缺项、意外工具或重复名。数据错误和业务失败使用受支持的 MCP 结果,不能把凭据和本地路径放进错误。当前支持 MCP text / image 内容;structuredContent 须为对象,由 Runner 投影为 json 内容。image 使用规范 Base64,单张解码后最多 256 KiB,MIME 为 PNG / JPEG / WebP / GIF / SVG。不接受任意 resource、audio 或文件块;以 provider/mcp.rs、provider/image.rs 为准。

    模型工具描述包含完整 plugin、installation、capability 和原始工具名称,帮助智能体区分同名工具;不要猜测前缀或搜索插件私有目录。Runner 收到的仍是原始名称。

    Runner 持续复用同一完整绑定的进程,并序列化其交换。配置版本或 binding 变化、超时、取消、禁用、更新、卸载和设备撤销会退休会话并清理辅助进程。插件不能自行跳过审批或向另一个安装借用配置。

    文件谁提供用途
    plugin.json作者身份、能力、资源引用与入口
    tools.json / configuration.json作者或实现导出不可变工具与原生配置契约
    package-manifest.jsonCLI pack/2 平台文件索引:role、size、sha256、入口及 Skill 归属
    package.tar.gzCLI pack同作者文件和程序的确定性 archive

    目录验证会生成同一运行索引;archive 验证核对全部字节与声明。导入在确切 Runner 上准备,服务端只保存 liyanlabs.plugin-native-metadata/1 声明、索引摘要及声明的公开图标,不接收程序或 Skill 字节。准备回执有期限且只能被一个安装认领;重试可重放同一操作。缓存缺失需在原设备重新导入。

    第一方可下载 runtime 仍由 Desktop 构建固定,Runner 按需验证并缓存。OpenSDL 使用相同 /2 文件索引,执行仍是已有 opensdl/1 gRPC 适配;没有人为改成 stdio provider。公共第三方下载发布不在本阶段。

    API 参考

    需要登录的插件 REST 根路径为 /liyanlabs/api/v1/plugins:

    领域规范路由
    目录GET /catalog;GET /catalog/:plugin_key/releases/:version
    安装GET/POST /installations;GET/DELETE /installations/:installation_id
    生命周期POST /installations/:id/{update,enable,disable,repair};GET /installations/:id/operations/:operation_id
    客户端模块GET /installations/:id/client-modules?platform=web(或 native);GET /installations/:id/client-modules/:module_key/resources/:resource_key?authority_digest=...;POST /installations/:id/client-modules/:module_key/tools/:tool_name
    初始化GET /installations/:id/setup;POST /installations/:id/setup/{assistant,reconcile}
    可观测性GET /installations/:id/health;GET /installations/:id/activity
    连接GET/POST /installations/:id/connections;detail GET/PATCH/DELETE;authorize/verify/revoke
    权限GET/PUT /installations/:id/permissions
    设备GET /devices;enrollment、activate、challenge、lease、ticket、revoke
    设备动作POST/GET /device-actions;claim、awaiting-confirmation、challenge、complete、fail、cancel
    外部效果GET /effects/:effect_id;decision 复用通用工具审批 API
    OAuth callbackGET /oauth/:provider/:region/:redirect_id/callback;当前 provider 交换未接通

    Runner 通道为 WSS /liyanlabs/ws/v1/runner/device。GitHub 公网 ingress 为 POST /liyanlabs/api/v1/plugin-webhooks/github/:endpoint_token。

    规范 REST 路径没有尾斜杠。请求和响应的事实源位于 service/pkg/web/views/plugins/、plugindevices/、runnerws/。

    安装和配置失败

    诊断时分别读取安装 operation、setup 与 health。包安装或确认失败、尚未配置和 provider 连接失败属于不同层,不应统一解释为“先配置才能启用”。

    观察含义恢复动作
    Operation 尚未完成或确认待 ACK服务端接受请求不代表 Runner 已完成并确认核对确切操作与设备回执;按界面重试同一次操作
    Setup 的 enabled 为 false包未取得当前确认核对包证据、release、设备和生命周期;更新/修复后重新启用
    Setup 的 requirements 包含 connection尚未完成所需设备配置在绑定 Desktop 的原生窗口配置
    Setup 的 requirements 包含 permissions尚未完成权限审查审核并保存当前 capability 权限
    Setup 要求 channel_agent已允许入站但尚未选 Agent明确选择入站 Agent,或撤销入站同意
    Setup 的 device_online 为 false精确设备绑定当前不可用恢复该 Desktop/Runner,不静默换机
    Setup 为 complete,provider health 不健康配置存在,但实时认证或连接未成功核对该设备的 provider、认证与连接状态

    新飞书包已启用而仍需配置属于正常状态。此时完成原生配置与权限审查,再 reconciliation;不要重复登记设备或轮换服务端 key。配置后仍无工具时,核对当前 release 的工具资源、完整发现匹配、权限和连接绑定。使用 Quickstart 的 doctor 查看本地包确认与已观察到的会话状态;它不是业务健康探测。

    设备连接失败

    设备 transport 与生命周期 readiness 必须分层排查:

    层错误检查内容
    Leaselease_failed登录 owner、enrollment state、generation、challenge/renewal
    Trust resourcetrust_config_invalidBuild channel、WSS origin/path、公钥有效期/撤销
    Server signerserver_signing_unavailableTyped channel signing 配置和 active key id
    Transporttransport_unavailableWSS 可达性、Redis 是否绑定当前 API Pod
    Authorityauthority_rejectedOwner/device/ticket/generation 与冻结 revision/digest
    Protocolprotocol_rejectedDesktop/Runner/service 协议版本和严格 frame shape

    不要把它们全部归为“租约问题”,每层的恢复动作都不同。

    数据库诊断

    只使用只读查询,不打印 secret、ciphertext、nonce、原始 provider payload、ticket 或本地路径。

    列出数据库实际插件表:

    SELECT tablename
    FROM pg_catalog.pg_tables
    WHERE schemaname = 'public'
      AND (tablename = 'plugin' OR tablename LIKE 'plugin\_%' ESCAPE '\')
    ORDER BY tablename;

    追踪 installation 和最新 operation:

    SELECT id, plugin_key, state, revision, release_id
    FROM plugin_installation
    WHERE user_id = '<user-id>'
    ORDER BY created_at DESC;
    
    SELECT id, kind, state, error_code, attempt, created_at, completed_at
    FROM plugin_installation_operation
    WHERE installation_id = '<installation-id>'
    ORDER BY created_at DESC;

    检查连接元数据和权限版本:

    SELECT id, provider_key,
           resource_scope_json->>'profile_label' AS profile_label,
           device_id, state, credential_id, credential_version, revision
    FROM plugin_connection
    WHERE installation_id = '<installation-id>';
    
    SELECT id, capability_id, revision, policy_digest, supersedes_id
    FROM plugin_permission
    WHERE installation_id = '<installation-id>'
    ORDER BY created_at DESC;

    检查设备在线租约:

    SELECT id, state, lease_generation, lease_expires_at,
           app_channel, app_version, catalog_digest
    FROM plugin_device_enrollment
    WHERE user_id = '<user-id>'
    ORDER BY updated_at DESC;

    调用不可用建议顺序:

    1. 最新 operation error code;
    2. health blockers;
    3. 当前 setup requirements;
    4. connection 与 permission coverage;
    5. 绑定 device lease/generation;
    6. 最后才查本地 runtime/package/provider health。

    新增插件

    普通本地工具在独立项目中实现,无需为新 provider 修改宿主。按 Quickstart 复制飞书,或使用 plugin init --template local-provider 创建空白项目。

    1. 声明稳定身份、版本、工具与配置资源,构建匹配设备的平台程序。
    2. 导出真实工具目录,静态验证、打包,并在选定 Desktop 导入。
    3. 使用原生配置和最小权限,验证一次真实业务调用。
    4. 验证目录漂移、取消、更新、禁用、离线与另一安装的隔离;写操作验证审批和不明结果对账。
    5. 提高版本重新打包分发,用户在原设备导入更新并重新启用。独立插件不需要宿主 PR 或官方 CI。

    新平台 Channel、新 Agent Runtime 及远程发布需同时设计声明、授权、运行和清理消费者。客户端模块使用 clientModules 声明,参考 plugins/science-data/client/ 的平台入口与 apply(ctx, config)。它们不能只通过增加 manifest 字段实现。支持范围见总览的能力类型;保持设备本地执行与无离线 fallback。

    第一方发行构建

    本节描述项目维护的官方发行路径;独立插件的本地创建、打包和导入不需要加入这个 registry,也不需要运行该工作流。

    第一方可下载插件通过 desktop/scripts/plugin-release-registry.mjs 的 registry 接入,声明工具、程序与 npm 包根。plugins.yaml 统一负责 plan/probe/build/publish;应用工作流只解析精确的已有发行。编译输出与安装目录归 job 私有,共享缓存只保留不可变、已验证的字节。Science data 的 provider 与 Web/Native 模块打包为同一个插件。官方可下载 runtime archive 与第三方导入包各有自己的清单契约;统一 CI 不表示任意官方下载文件都可直接拿来本地导入。第三方应通过 liyan plugin pack 生成导入包。

    源码索引

    关注点源码
    内置目录与校验摘要service/pkg/core/plugin/builtin/
    CLI 与包契约runner/crates/liyanlabs-cli/src/plugin.rs、runner/crates/liyanlabs-plugin/
    Capsule 模块与授权service/pkg/core/plugin/clientmodule/、frontend/shared/src/core/pluginClientModules/
    统一发行desktop/scripts/plugin-release-registry.mjs、.github/workflows/plugins.yaml
    扩展 schemaservice/pkg/core/plugin/packagevalidator/schemas/plugin-extension.schema.json
    生命周期与 readinessservice/pkg/core/plugin/lifecycle/、service/pkg/repo/plugin/lifecycle/
    当前账户调用条件service/pkg/repo/plugin/currentauthority/
    Connection、permission、secretservice/pkg/repo/plugin/profile/、service/pkg/repo/plugin/permission/、service/pkg/repo/plugin/secret/
    Device 与 actionservice/pkg/core/plugin/device/、service/pkg/repo/plugin/device/
    设备桥与 provider RPCservice/pkg/core/plugin/devicebridge/、service/pkg/core/plugin/localprovider/
    外部 effectservice/pkg/core/plugin/effect/、service/pkg/repo/plugin/effect/
    Runtime Session 与 eventservice/pkg/core/plugin/runtime/、service/pkg/repo/plugin/runtime/
    HTTP/WSS viewservice/pkg/web/views/plugins/、service/pkg/web/views/plugindevices/、service/pkg/web/views/runnerws/
    Web 插件 UIfrontend/web/src/features/plugins/、frontend/shared/src/service/pluginService.ts
    Runner 执行runner/crates/liyanlabs-runner/src/plugins/
    Electron 凭据desktop/electron/src/plugins/
    Dev 签名与信任配置desktop/scripts/provision-dev-plugin-runner.mjs
    包确认与回执service/pkg/core/plugin/lifecycle/activation.go