plugin.json 完整参考
manifest 的类型单源是 @xinghan/plugin-sdk 的 PluginManifest(packages/plugin-sdk/src/types/manifest.ts)。本页与之对照维护;冲突时以类型定义为准。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | ✓ | reverse-DNS 形态全局唯一,如 xinghan.pomodoro;本地开发用 dev. 前缀 |
name | string | ✓ | 显示名 |
version | string | ✓ | 版本号 |
description | string | 一句话说明(市场卡片、插件列表用)。与 market 同为市场消费的扩展字段 —— PluginManifest 类型未声明它们,loader 原样透传 | |
runtime | 'node' | 'static' | ✓ | 运行时(见插件解剖) |
entry | string | node 必填 | 后端启动入口(相对插件根),如 dist/server.js |
devEntry | string | dev 模式后端入口(如 TS 源码,tsx 直跑);缺省用 entry | |
ui | string | static 必填 | 静态 UI 根目录(相对插件根),如 ui |
capabilities | CapabilityName[] | ✓ | 能力白名单(可为空数组,但必须是数组) |
contributes | object | 贡献声明(下详) | |
market | object | 市场元数据:category / tags / featured |
capabilities 取值
Section titled “capabilities 取值”'dataEngine' · 'llm' · 'storage' · 'events' · 'secrets' · `satellite:${kind}`
satellite:<kind> 是卫星项目作用域:声明后才能经 capability 访问目标为 <parent>#<kind> 的卫星项目(如 Agent 会话库要 satellite:agent)。未声明访问任何卫星一律 403。
contributes.views
Section titled “contributes.views”一块 iframe UI 及其挂载位置。
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 插件内唯一 view id |
title | string | 显示名 |
icon | string | lucide 图标名(可选) |
location | ViewLocation | right-drawer / activity-bar / settings / fullscreen / floating / bottom-panel |
entry | string | iframe 入口(相对插件根,支持 hash 路由) |
devUiUrl | string | dev 模式 iframe 直连地址(如 vite dev server),宿主反代到它 |
activity-bar:左侧活动栏图标 → 占满编辑区的全屏页,URL 深链 /projects/:id/page/<pluginId>/<viewId>。settings:嵌进宿主偏好设置页 —— 复杂配置 UI 用它,简单 key/value 用 contributes.settings 自动表单。
"views": [ { "id": "panel", "title": "工具", "icon": "clock", "location": "right-drawer", "entry": "ui/index.html", "devUiUrl": "http://localhost:3001" }]contributes.explorerSections
Section titled “contributes.explorerSections”新工作台布局(左侧导航 + 分栏)里,「功能」段的每一行都是可展开的(仿 VS Code 侧栏 section):一行标题 + 折叠箭头,展开后是一份列表或插件自己的视图。经典布局忽略这个字段,仍按 activity-bar 图标渲染。
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 插件内唯一 |
title | string | 显示名(支持多语言 map) |
icon | string | lucide 图标名(可选,缺省用 open 指向视图的图标) |
render | list / view | 展开后怎么渲染:list = 宿主通用列表;view = 插件自己的 iframe |
items | string | render:list:插件后端端点(相对插件根)。宿主 GET <端点>?projectId=<pid>&limit=&cursor=&q=,期望 { ok, data:{ items, nextCursor?, total? } }。limit 一页条数;cursor 是上一页回的 nextCursor(不透明字符串,插件自定义,比如 offset),回包没有 nextCursor 就是到底了;不认这两个参数的端点照旧一次回全部。total 可选,段头显示总数用 |
entry | string | render:view:iframe 入口(相对插件根,支持 hash 路由) |
open | string | 标题行右侧「打开页面」去的本插件 view id。activity-bar 视图 → 全页;right-drawer 视图 → 工作台的一格 |
searchable | boolean | render:list:列表支持服务端搜索。宿主在段头给搜索框,把 q 交给 items 端点由插件在库里搜(分页照常);不声明的话宿主只在已加载的条目里本地过滤 title / subtitle |
声明了 open 的 section 会顶替那个视图原本在导航里的一行(图标行变成可展开行);没被认领的 activity-bar 视图仍是一行普通入口,老插件零改造。
items 端点返回的每一项:
| 字段 | 类型 | 说明 |
|---|---|---|
id / title | string | 必填 |
subtitle | string | 行尾灰字(时间、编号…) |
icon | string | lucide 图标名 |
badge | string / number | 行尾强调(如运行中 ●) |
group | string / 多语言 map | 分组小标题(如「今天 / 昨天 / 本周 / 更早」)。分组语义由插件决定,宿主只在相邻两项 group 变化处画一行小标题;没有 group 的项不画 |
action | object | 点一项做什么,宿主执行:{ kind:'view', view, arg? } 打开本插件的视图并把 arg 交给它(iframe 收 context.openArg;agent 原生壳按 arg.sessionId 切会话);{ kind:'file', nodeId } 打开项目里的文件;{ kind:'route', to } 宿主前端路由;{ kind:'url', url } 外链新窗口 |
列表变了要宿主重拉时,插件 iframe 向父窗口 postMessage({ type: 'explorer/refresh' });不发的话只在展开、切项目时拉一次。
"explorerSections": [ { "id": "sessions", "title": { "zh-CN": "会话", "en-US": "Sessions" }, "icon": "bot", "render": "list", "items": "api/nav/sessions", "searchable": true, "open": "workbench" }]contributes.commands
Section titled “contributes.commands”| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 命令 id(命令消息回传用) |
title | string | 显示名 |
key | string | 快捷键,mac 记法(cmd+l),其它平台宿主自动 normalize 成 ctrl |
"commands": [ { "id": "newConversation", "title": "新建对话", "key": "cmd+l" }]contributes.settings
Section titled “contributes.settings”简单配置项 —— 宿主偏好页按插件分组自动渲染表单,插件用 cap.storage.get('settings:<key>') 读值。
| 字段 | 类型 | 说明 |
|---|---|---|
key | string | 存储 key(自动加 settings: 前缀) |
label | string | 表单 label |
type | 'string' | 'secret' | 'boolean' | 'enum' | 'number' | 控件类型(secret=密码框) |
description | string | label 下方帮助文字 |
options | string[] | enum 必填 |
min / max / step | number | number 可选 |
default | 值 | UI 显示的默认值(不写入存储,插件代码自己 ?? 兜底) |
placeholder | string | string/number 占位符 |
"settings": [ { "key": "apiUrl", "label": "服务地址", "type": "string", "placeholder": "https://api.example.com" }, { "key": "apiKey", "label": "API Key", "type": "secret" }, { "key": "mode", "label": "模式", "type": "enum", "options": ["fast", "accurate"], "default": "fast" }]插件读值:await cap.storage.get<string>('settings:apiUrl', { actor })。
contributes.contextMenus
Section titled “contributes.contextMenus”右键菜单项(详见右键菜单与命令)。
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 点击后随命令消息回传 |
title | LocalizedText | 单串或 { 'zh-CN': …, 'en-US': … },可含 {count} |
points | ContextMenuPoint[] | table.rows / table.cells / table.columnHeader / fileTree.table / fileTree.node |
modal | { view, width?, height? } | 以模态框打开某 view(默认是打开右抽屉) |
"contextMenus": [ { "id": "translate", "title": { "zh-CN": "翻译选中 {count} 格", "en-US": "Translate {count} cells" }, "points": ["table.cells"] }]contributes.fieldTypes
Section titled “contributes.fieldTypes”自定义字段类型。核心契约:字段骑在基础类型上存储(baseType: text/number/date/richtext 决定引擎里的真实 type 码),排序/筛选/移动端/未装插件的协同端全按基础类型工作,插件只负责渲染与编辑。字段身份与配置存在 field.properties.xPluginField。
| 字段 | 类型 | 说明 |
|---|---|---|
typeKey | string | 插件内唯一;全局身份 = ${pluginId}:${typeKey} |
name | LocalizedText | 类型名 |
icon | string | lucide 图标(缺省用基础类型图标) |
baseType | PluginFieldBaseType | 基础存储类型 |
editor | { view, size?, placement? } | 双击 cell 的编辑弹窗;placement: anchor(锚到 cell)/ center(默认)。插件 ui.resolveModal({ value }) 回传新值,宿主走 op 系统写入 |
fieldConfig | { view, height?, maxBytes? } | 建/改字段时的配置面板;config 进 field.properties 随协同与历史传播,maxBytes 是它的护栏 |
drawerBlock | { view, height? } | 记录详情抽屉里的字段块;缺省=摘要文本+编辑按钮 |
summaryPath | string | 从 cell JSON 取摘要文本的 path(如 $.summary)—— 网格/导出/未装插件端显示用 |
columnMenus / cellMenus | PluginFieldMenuItem[] | 仅该类型字段出现的菜单(cellMenus 支持 requiresValue;modal context 带 cellValue) |
defaultValue | unknown | 新 cell 默认值(缺省 null) |
validation.maxBytes | number | 单值写入护栏,缺省 256KB |
hideBaseTypeInPicker | boolean | true=本类型是基础类型的升级替代,新建字段选择器里隐藏对应基础类型;卸载后自动回归 |
最小可用声明(内置 JSON 字段的真实形态):
"fieldTypes": [ { "typeKey": "jsonDoc", "name": { "zh-CN": "JSON", "en-US": "JSON" }, "icon": "braces", "baseType": "text", "editor": { "view": "editor", "size": { "width": 760, "height": 560 }, "placement": "center" }, "validation": { "maxBytes": 1048576 } }]完整闭环见自定义字段类型。
contributes.fileTypes
Section titled “contributes.fileTypes”自定义文件类型:出现在「新建」菜单,编辑区由插件 iframe 渲染。认领语义:fileType 与内建同名(如 markdown)= 升级替代 —— 新建入口替换内建、已有节点渲染也接管;卸载后自动回归内建编辑器,节点零迁移。
| 字段 | 类型 | 说明 |
|---|---|---|
fileType | string | 内建同名=认领;新类型用独立名 |
name | LocalizedText | 类型名 |
icon | string | 宿主图标表名(缺省通用文件图标) |
color | string | number | 宿主色板槽名(table/doc/json/automation/ai/dashboard/image)或色相角 0-360(宿主按主题算亮度彩度)。不接受裸 hex —— 写死的颜色在暗色主题必然失衡 |
suffix | string | 显示后缀(纯展示) |
storage | 'text' | 底层存储形态(节点内容=文本,经 doc.load/save 读写) |
entry | string | 编辑区渲染入口(iframe) |
"fileTypes": [ { "fileType": "mindmap", "name": { "zh-CN": "思维导图", "en-US": "Mind map" }, "icon": "braces", "color": "doc", "suffix": "mm", "storage": "text", "entry": "ui/index.html#/doc" }]contributes.fileRenderers
Section titled “contributes.fileRenderers”只接管某 fileType 的渲染(不进新建菜单):{ fileType, entry, label? }。宿主命中该类型时挂插件 iframe 并经 context 桥推入节点 id。
contributes.tableViews
Section titled “contributes.tableViews”同一张表的另一种投影(日历/画廊/地图/甘特…)。视图实体的 type 存 ${pluginId}:${viewKey};「+新建视图」菜单在内建类型后列出;激活时表编辑区渲染插件 iframe,context 带 { tableNodeId, viewId };视图配置存视图的 styleInfo。未装插件时该视图 tab 显示占位,数据不丢。
| 字段 | 类型 | 说明 |
|---|---|---|
viewKey | string | 插件内唯一 |
name | LocalizedText | 视图名 |
icon | string | 单 emoji |
entry | string | 渲染入口 |
writes | object | 写入能力声明(缺省只读):cells(改本表已有格)/ addRecords / deleteRecords(单独一档,不可逆)/ maxBytes(缺省 256KB)。宿主按档放行且锁定本视图挂载的表 |
"tableViews": [ { "viewKey": "calendar", "name": { "zh-CN": "日历", "en-US": "Calendar" }, "icon": "📅", "entry": "ui/index.html#/calendar", "writes": { "cells": true } }]contributes.automationNodes
Section titled “contributes.automationNodes”自定义自动化节点:声明式配置面板(宿主内建控件渲染,不是 iframe —— 因为 valueSelector 要接宿主的上游变量域)+ 后端执行。身份 = ${pluginId}:${typeKey} 即 node.Type 存储值。
| 字段 | 类型 | 说明 |
|---|---|---|
typeKey | string | 插件内唯一 |
claim | string | 认领内建节点名(v1 仅 llm):在场时接管入口/面板/执行,node.Type 照存内建名,存量零迁移;不在场回退内建 |
version | number | 配置结构版本(插件自己向后兼容,宿主不做迁移) |
name / description | LocalizedText | description 兼作 AI 工具描述,按写给模型看的质量要求 |
icon | string | lucide svg path 或单 emoji |
category | string | 归入宿主分类;缺省 plugin 分组 |
configSchema | NodeConfigField[] | 配置面板字段(下详) |
outputSchema | OutputVarDecl[] | 输出变量静态声明(下游 valueSelector 编辑期消费) |
execution | object | timeoutMs(缺省 120s,上限 30min)/ maxConcurrency(缺省 4,超出排队)/ idempotent(缺省 false=至多一次,防收费副作用重复)/ supportsDryRun / sideEffects(none/table/external) |
NodeConfigField.widget 取值:text / textarea / number / switch / select / valueSelector(可插上游变量)/ tableSelector / fieldSelector(dependsOn 指向同面板 tableSelector)/ optionSelector / optionWeightMap / keyValueList / dynamicSelect(选项来自插件后端 GET /automation/<typeKey>/options)。
前向兼容承诺:宿主碰到不认识的 widget 名,把该字段降级成 text 输入并告警,绝不丢弃整个节点声明 —— 插件可放心用新 widget,无需探测宿主版本。
最小可用声明(一个”翻译文本”节点):
"automationNodes": [ { "typeKey": "translate", "version": 1, "name": { "zh-CN": "翻译文本", "en-US": "Translate" }, "description": { "zh-CN": "把输入文本翻译成目标语言" }, "category": "integration", "configSchema": [ { "key": "text", "label": { "zh-CN": "文本" }, "widget": "valueSelector", "required": true }, { "key": "target", "label": { "zh-CN": "目标语言" }, "widget": "select", "options": [ { "value": "en", "label": { "zh-CN": "英语" } }, { "value": "ja", "label": { "zh-CN": "日语" } } ], "default": "en" } ], "outputSchema": [ { "key": "translated", "type": "string", "label": { "zh-CN": "译文" } } ], "execution": { "timeoutMs": 30000, "sideEffects": "external" } }]执行侧在插件后端用 defineAutomationNode() 注册(@xinghan/plugin-sdk/server 导出),宿主把节点任务经 POST /automation/<typeKey> 投给你的进程。
contributes.contributionPoints / contributions
Section titled “contributes.contributionPoints / contributions”插件间扩展面的声明式形态(与运行时 contributions capability 等价,host 加载时代为注入,下线整体退场):
contributionPoints[]:开放一个点。point必须以本插件 id 为前缀(<pluginId>/<name>,所有权即命名空间);schema做顶层必填字段浅校验。contributions[]:向某个点投稿纯数据(point可以是其它插件的点,点未声明时挂 pending);data≤64KB,语义由点的 owner 定义。
static 插件(无进程)只有这条声明式的路;node 插件两条路并存,纯数据投稿优先写 manifest(可被市场/审核静态检视)。