创建第一个插件
生成独立 Go 项目并验证节点执行
本教程生成一个 Windows Process 插件:工作流触发它的 in 端口,它完成一次调用后走 done 分支。这是可安装的起点;你的业务逻辑在此基础上实现。需要面板时可以同时生成完整的计数器 provider 示例。
准备 Python 3.10+、PowerShell 7、Go,以及与你的宿主版本匹配的 Yotta 源码或开发工具。Go 版本以该版本的 go.mod 为准。下面使用 PowerShell,路径可替换为自己的目录。
1. 获取固定 SDK
开发未发布的 Yotta 版本时,在 Yotta 仓执行:
task plugins:export-sdk命令返回 JSON,其中 version 是基于内容的不可变开发版本,proxy 是 file:///... 本地 Go module proxy 地址。保留这两个输出。它是本地 SDK 分发产物,不代表已经发布到公共模块服务。
在开发 shell 中设置,替换示例占位值:
$env:GOPROXY = '<上一步的 proxy>,https://proxy.golang.org,direct'
$env:GONOSUMDB = 'github.com/yottaapp/yotta'如果 shell 已配置其他 GONOSUMDB 条目,请追加而不是覆盖。已有正式可获取 SDK 版本时,直接固定那个版本,使用其分发说明,不必导出本地 SDK。
2. 创建独立项目
在 Yotta 仓执行下面命令。--output 必须是尚不存在的目录;命令不会覆盖已有项目。
python scripts/create-plugin.py `
--output E:/projects/my-yotta-plugin `
--module example.com/my-team/my-yotta-plugin `
--namespace https://plugins.example.com/publishers/my-team `
--slug my-plugin `
--name '我的插件' `
--sdk-version '<上一步的 version>'namespace 示例只适用于自己的本地实验。准备市场发布时,应从作者账号获取真实 publisher namespace,并从项目创建时就保持稳定。module 是 Go 源码依赖路径;它与市场 publisher namespace 是不同概念。
添加 --with-panel --panel-port 18760 会生成带按钮交互的计数面板。端口由你选择,应与其他 companion 区分。
进入生成目录:
Set-Location E:/projects/my-yotta-plugin
go mod tidy
./build.ps1 -Action Check
./build.ps1 -Action Build3. 理解项目
| 文件 | 什么时候改 |
|---|---|
plugin.json |
作者 namespace、包名称、版本、说明和面板端口 |
project.go |
将包配置嵌入程序,供节点和打包器使用同一 effect 身份 |
cmd/node/main.go |
节点每次被工作流调用时的业务逻辑 |
cmd/package/main.go |
节点合同、输入输出、配置 schema、执行与目标声明 |
cmd/package/panel.go |
可选 companion 与面板的打包声明 |
internal/paneldefinition/ |
带面板项目的字段、组件、事件和双语标签 |
cmd/companion/ |
带面板项目的持续服务、HTTP 接口及测试 |
build.ps1 |
统一检查、交叉编译、密钥创建和打包 |
AGENTS.md |
AI 在这个独立项目工作时的开发入口 |
生成的 go.mod 已固定 SDK 版本;保留 go.sum。bin/、dist/、本地产物和密钥不会进入版本控制。
4. 实现节点
Guest 的基本顺序是 NewGuest → ReceiveInvocation → 处理业务 → Succeed 或 Fail。stdout 专供协议帧,普通诊断输出到 stderr。
节点配置和包配置不同:plugin.json 是构建元数据;工作流编辑器填写的节点配置由合同的 JSON schema 定义,在 invocation.ConfigJson 中读取。添加配置时同步修改 schema 和解析逻辑。不要把用户的本地路径或凭据写进可分发合同。
模板使用 effect 节点合同,每个声明的 effect 在一次调用中必须恰好有一条 guest.Record 执行记录,且 EffectId 与合同完全一致。记录真实的 succeeded、failed 或 cancelled 结果,再返回最终结果;只调用 Succeed 而遗漏记录会导致宿主报 runtime.journal_failed。模板的空操作也完成这一协议步骤。不要无论业务是否成功都保留模板的成功记录。
添加数据输入输出时,使用 authoring.Builtins() 得到该 SDK 的准确类型,以及 DataInputPort / DataOutputPort 定义端口。实际输入位于 invocation.Inputs,值使用 Value Envelope;可以通过 InlineJSON 读取,在类型不变的变换中用 ReplaceInlineJSON 构造输出。最终返回的端口名和类型必须与合同一致。Yotta 仓 examples/plugins/process-uppercase/main.go 展示字符串输入、转大写和类型保留输出。
需要宿主目标时,在合同中声明 ConfiguredTargetSpec 和相应 host feature,再使用 OpenTarget、Invoke、Drop。用户的目标配置提供连接参数;插件不再要求逐节点重复授权。具体 kind、操作和 payload 以固定 SDK 与宿主合同为准,不能猜测字符串。
返回错误前声明匹配的错误合同、失败路由和中英文文案。保留取消与超时语义,完成或释放当前调用后再退出。不要在节点返回后留下脱离宿主调度的后台任务;持续采集见后台服务与面板。
5. 验证到真正运行
运行 Check 测试项目;带面板模板还验证协议、重复点击、并发重放、旧会话、过期控制版本和有界回执。业务逻辑改变后补充自己的有效输入、边界、错误和取消测试。
按打包指南生成 .ynp,在 Yotta 的“设置 → 插件”导入,重启后添加该节点,连接“Run 开始”的 started → 节点 in。执行成功并走到 done,才算完成首次节点验证。带面板项目还应打开面板、点击按钮并确认计数仅增加一次。
在维护 Yotta 本身的环境中,可使用已有的 cmd/plugin-management-smoke,传入 -archive、全新的 -root 和正式构建的 -host:它执行导入、重开 profile、编辑工作流和真实 Run。这个工具适用于当前 starter 的无必填配置、in 触发接口;有必填数据或外部设备的插件需要自己的对应旅程。不要对正在使用的 profile 运行 smoke,也不要把编译成功描述成运行成功。