Capability API 总览
node 插件的后端经 CapabilityClient 访问宿主能力。类型单源在 @xinghan/plugin-sdk/server(capability-client.ts),本页是导航图;方法签名以类型定义与编辑器提示为准。
import { CapabilityClient } from '@xinghan/plugin-sdk/server'
const cap = new CapabilityClient({ baseUrl: process.env.CAPABILITY_BASE_URL, // 宿主注入 token: process.env.CAPABILITY_TOKEN,})所有方法 (args, ctx) 两段式,ctx = { actor, projectId }。失败抛 CapabilityError(结构化 code + message)。
命名空间一览
Section titled “命名空间一览”| 命名空间 | 需声明 | 管什么 |
|---|---|---|
cap.dataEngine | dataEngine | 表格数据与结构(下详) |
cap.llm | llm | ⚠️ 暂未接线(SDK 有客户端、宿主尚未挂载路由,调用 404)。插件用 AI 的现实路径是 Agent 微对话通道 |
cap.storage | storage | 插件私有 KV(get/set;settings: 前缀与宿主配置表单互通) |
cap.events | events | 订阅宿主事件 |
cap.file | — | 资产库:uploadAssetRaw / downloadAssetRaw(图片/附件字节) |
cap.fileTree | — | 文件树:list / create / move / rename、文档内容 getContent / saveContent、版本 saveDocVersion / listDocVersions / restoreDocVersion |
cap.log | — | append-only 日志流:append(记结构化事件)/ range(按序读回)/ count / delete —— 插件自己的操作留痕/回放用 |
cap.search | — | 项目内搜索(与页内搜索同引擎) |
cap.automation | — | 自动化节点执行侧(配合 defineAutomationNode 注册执行路由) |
cap.dataPr | — | 数据审批单:create / get(把写入变成待审提议而非直写) |
cap.share | — | 分享令牌作用域下的受限访问(分享页场景) |
cap.render | 仅内置插件 | 离屏渲染截图。第三方插件调用被拒(能截任意页面的能力不外开);无 Electron 环境返回 not_supported |
cap.sandbox | — | 沙箱执行:跑不可信/生成代码的受限环境(Agent 的 run_sandbox 底座,网络与文件面受护栏) |
cap.contributions | — | 运行时贡献点注册/投稿 —— 与 manifest 声明式形态等价(见与 Agent 集成),node 插件想动态增删投稿时用运行时 API,纯数据投稿优先写 manifest |
cap.pluginDev | 仅内置插件 | 开发辅助(dev 插件目录写入等) |
capabilities数组是申报面:市场审核与用户安装时看到的授权清单,请如实只报真用到的。运行时强制目前覆盖satellite:<kind>(未声明访问卫星一律 403);其余命名空间的运行时强制在路线上 —— 别把”没声明”当运行时会拦的保证。
cap.dataEngine 方法速查
Section titled “cap.dataEngine 方法速查”读:getFields · getViews · queryRecords(过滤/排序/分页在 opts: QueryOptions 里)· getRecord · getRecords · aggregate
写:updateCell · updateCells(批量=一次撤销)· addRecord · addRecords · deleteRecord
结构:addField · addFields · addTable · createView
底层:applyChangeset(直接提交 changeset —— 上面的写方法都是它的糖;一般用不到)
await cap.dataEngine.queryRecords( { tableNodeId, opts: { filters: [{ fieldId, op: 'contains', value: '北京' }], sorts: [{ fieldId, desc: true }], limit: 100, offset: 0, }, }, { actor, projectId },)用法与治理语义见读写表格数据。
cap.storage
Section titled “cap.storage”插件私有 KV(按 actor 隔离);settings: 前缀的 key 与宿主偏好页的自动表单互通:
await cap.storage.set('note', '内容', { actor })const note = await cap.storage.get<string>('note', { actor }) // 没有时 nullconst apiUrl = await cap.storage.get<string>('settings:apiUrl', { actor }) // 用户在偏好页填的await cap.storage.delete('note', { actor })cap.llm(暂未接线)
Section titled “cap.llm(暂未接线)”SDK 定义了 cap.llm.chat 客户端,但宿主尚未挂载对应路由 —— 当前调用会 404。插件借用 AI 的现实路径是 Agent 微对话通道(UI 侧 createMicroClient/mountMicroPanel,复用用户在 Agent 里配好的模型,见与 Agent 集成)。本节保留占位,接线后更新。
cap.fileTree
Section titled “cap.fileTree”文件树与文档内容。做”批量整理/文档生成器”类插件的主面:
// 树操作const nodes = await cap.fileTree.list({ projectId }, ctx) // 全树await cap.fileTree.create({ projectId, name, fileType /* … */ }, ctx)// ⚠️ 目标父节点参数叫 newParentId;写错名(如 parentId)不报错 ——// 宿主把缺失当 ''(项目根),节点被**静默搬到根目录**await cap.fileTree.move({ projectId, nodeId, newParentId }, ctx)await cap.fileTree.rename({ projectId, nodeId, name }, ctx)
// 文档内容(text 存储形态的节点:markdown/画板/导图…)// ⚠️ 参数名注意:读按 filePath,写与版本按 fileId —— 传错宿主直接拒const { content } = await cap.fileTree.getContent({ projectId, filePath }, ctx)await cap.fileTree.saveContent({ projectId, fileId, content }, ctx)
// 文档版本:保存命名快照、列出、回滚 —— 给"重要节点存个版本"类功能用await cap.fileTree.saveDocVersion({ projectId, fileId /* label */ }, ctx)const versions = await cap.fileTree.listDocVersions({ projectId, fileId }, ctx)await cap.fileTree.restoreDocVersion({ projectId, fileId, versionId }, ctx)UI 侧(iframe 里)操作当前文件用 ui 桥的
docLoad/docSave更顺手(见文件类型指南);后端的 fileTree 面向跨文件批量场景。
cap.dataPr:审批单完整流程
Section titled “cap.dataPr:审批单完整流程”把写入变成待审提议而不是直写 —— 大改动(“agent 建议改 500 行”)的正路:
// ① 开一张审批单(projectId 在 ctx 里,args 只有 title/description)const { prId } = await cap.dataPr.create( { title: '批量清洗手机号格式' }, ctx,)
// ② 写入统统带 prId → 变成提议,不落 live 数据await cap.dataEngine.updateCells({ tableNodeId, prId, cells }, ctx)await cap.dataEngine.addFields({ tableNodeId, prId, fields }, ctx) // 新列返回 tfld_ 临时 id
// ③ 用户在审批中心(GitHub PR 式页面)逐项看 diff → 合并或拒绝// 读方法带 prId 可做"叠加提议值"的预览(queryRecords/getRecord 的读分叉)合并/拒绝是用户的动作,插件到 ② 为止 —— 别试图替用户合并。
卫星项目:satellite:<kind>
Section titled “卫星项目:satellite:<kind>”项目可以挂卫星库(id 形如 <父项目>#<kind>)—— Agent 的会话记录库就是 #agent 卫星。插件要读写某类卫星,manifest 必须声明 satellite:<kind>(如 satellite:agent),否则一律 403。这是审计数据的最小权限闸:普通插件默认碰不到 Agent 的对话史。
cap.events
Section titled “cap.events”订阅项目里资源(表等)的变更 —— 做”数据变了就响应”的插件(同步器、通知器、事件驱动 agent)用它,别轮询:
const stop = cap.events.subscribe( { projectId, resourceIds: [tableNodeId], actor }, (env) => { // env: { event, data } —— changeset 变更/热层帧等信封 if (isSelfActor(env, actor)) return // ⚠️ 防环:跳过自己写入触发的事件 handleChange(env) }, { onClosed: (reason) => console.warn('事件流断开:', reason) },)// 不用了记得 stop()isSelfActor(SDK 同处导出)是必修课:事件驱动插件最容易犯的错是”自己写 → 收到自己的事件 → 再写”死循环。SSE 的断线重连、心跳判死 SDK 都处理好了,只管消费。
iframe 里的前端从 @xinghan/plugin-sdk/ui 拿宿主上下文与交互桥:
import { installContextBridge, getContext, onContextChange } from '@xinghan/plugin-sdk/ui'HostContext 字段:
| 字段 | 何时有值 | 含义 |
|---|---|---|
projectId | 恒有(面板必在项目内打开) | 当前项目 |
activeNodeId / activeFileType / activeNodeName | 编辑器有激活节点时 | 用户正在看的表/文件 |
tableNodeId | tableViews 挂载时 | 本视图渲染的表 |
viewId | tableViews 挂载时 | 视图实体 id(styleInfo 按它存取) |
nodeId | fileTypes/fileRenderers 挂载时 | 本 iframe 绑定的文件节点 |
其它 UI 桥模块:micro-panel(向 Agent 皮肤投稿微面板)、modal-chrome(字段编辑器/配置面板等模态的 resolve/reject 协议)。字段编辑器的 ui.resolveModal({ value }) 契约见 fieldTypes 参考。
UI ↔ 你自己的后端不经任何桥 —— 就是 HTTP。UI 里用相对路径(fetch('../api/xxx')),宿主把 /plugins/<id>/* 反代到你的进程;用户身份在请求头 x-actor。