插件开发指南
开发自有插件接入 LabCode:七类通用通道、工具链检测、类型化能力、命令 / 视图 / 项目类型声明、manifest 契约与示例。
LabCode 的插件系统面向第三方开放:任何开发者都可以编写插件,把自定义工具链接入 AI 智能体与 UI,无需修改 LabCode 本体。插件通过 manifest.json 声明工具、类型化能力、命令、视图与项目类型,宿主统一分发渲染——UI 与 AI 同源获得插件能力。
插件结构
一个插件就是一个目录(安装后位于 %APPDATA%\LabCode\plugins\{id})。目录按需存在,只有 manifest.json 是必填的:
my-toolchain/
├── manifest.json # manifest(必填)
├── ui/ # Web 视图页面(views.webview 的 entry,可选)
├── tools/ # cli 插件的可执行脚本 / 二进制(可选)
├── proto/ # gRPC 插件自带的 .proto(可选,复用宿主服务则不需要)
└── assets/ # 插件自带资源(如 lib/ 库文件;可选)
bin/(dependencies 下载的二进制),不属于插件打包内容。manifest.json 最小结构
{
"id": "my-toolchain", // kebab-case,全局唯一
"name": "我的工具链",
"version": "1.0.0",
"developer": "你的名字",
"icon": "🔧",
"capabilities": {
"compile": { "transport": "internal", "service": "arduinoCli", "method": "compile", "output": "avr-gcc" },
"board-manage": { "transport": "internal", "service": "arduinoCli", "method": "board.search" }
}
}
安装时宿主 validateManifest 校验结构:能力 keys、工具 transport、命令 kind/placement、项目类型 scaffold 路径安全(拒绝绝对路径 / .. 穿越)、视图 entry 等,非法 manifest 拒绝安装。
插件图标
manifest.json 的 icon 字段声明插件图标,出现在活动栏、插件管理列表、市场卡片、新建项目页。支持三种形式:
| 形式 | 说明 | 示例 |
|---|---|---|
| emoji | 直接写 emoji 字符 | "icon": "🔧" |
| lucide 图标名 | 宿主渲染层映射 lucide 线条图标(小写 kebab) | "icon": "box" |
| 品牌标识 | 品牌 LOGO:在宿主渲染层注册 React 组件,icon 写标识字符串 | "icon": "wokwi" |
七类通用通道
每个工具通过 transport 声明走哪类通道。所有通道都是宿主通用驱动,插件只需声明,无需宿主预注册。
| transport | 含义 |
|---|---|
cli | 一次性命令行(spawn 数组参数防注入) |
http | REST API(全局 fetch,尊重代理) |
grpc | 插件自带 .proto,宿主动态加载连接插件 daemon |
mcp | 调用插件声明的 MCP 服务器工具 |
mqtt | 通用 MQTT 设备通道(连接 broker / 订阅 / 发布 / 请求-响应) |
internal | 宿主标准能力(串口 / 日志 / 状态 / Arduino / toolchain) |
backend | 调用插件自声明的 stdio 后端(JSON-RPC over stdin/stdout) |
1. cli — 命令行工具(最常用)
一次性命令行工具。宿主用 spawn(数组参数,不经 shell,防注入)执行:
{
"name": "flash",
"description": "烧录固件到设备",
"parameters": {
"type": "object",
"properties": {
"hexPath": { "type": "string", "description": "固件文件路径" }
},
"required": ["hexPath"]
},
"transport": "cli",
"category": "execute",
"cli": {
"command": ["esptool.py", "--port", "{port}", "write_flash", "0x0", "{hexPath}"],
"parse": { "type": "json", "pick": "result" },
"successCodes": [0],
"timeoutMs": 30000
}
}
command[0]为可执行文件,其余为参数模板;{参数名}占位符被工具参数替换(值永远作为单个 argv,天然防 shell 注入){defaultPort}/{defaultFQBN}= 当前选中的串口 / 开发板;工具链插件还有{settings.toolchain.active.*}、{projectPath}、{env.PATH}等占位符- 必须声明
timeoutMs:cli 驱动默认 30s,长编译 / 烧录会误判超时 stream: true+streamChannel:逐行实时输出;pty: true在真终端打开交互命令;pauseSerialPort: true烧录前暂停同端口后台串口日志
2. http — REST API
{
"name": "query_sensor",
"description": "查询传感器数据",
"transport": "http",
"category": "query",
"http": {
"url": "https://api.example.com/sensors/{id}/data",
"method": "GET",
"query": { "key": "{apiKey}" },
"headers": { "Authorization": "Bearer {token}" },
"parse": { "type": "json", "pick": "data.value" },
"timeoutMs": 10000
}
}
3. grpc — 动态 proto(自带 .proto)
{
"name": "compile",
"description": "编译工程",
"transport": "grpc",
"category": "execute",
"grpc": {
"service": "mypkg.ToolService",
"method": "Compile",
"proto": "proto/tool.proto",
"backendId": "default"
},
"backends": [
{
"type": "grpc",
"command": "mytool-daemon",
"port": 0,
"args": ["daemon", "--port", "0"],
"readiness": { "kind": "grpc", "timeoutMs": 20000 }
}
]
}
service 用 proto 内全限定名,proto 为插件目录内 .proto 相对路径;宿主只连接该插件自己声明的 backend 端口(安全边界)。
4. mcp — 调用 MCP 服务器工具
{
"mcpServers": [
{ "name": "browser", "command": "npx", "args": ["-y", "@playwright/mcp"], "autoApprove": false }
],
"tools": [
{
"name": "browser_navigate",
"description": "打开网页",
"transport": "mcp",
"category": "query",
"mcp": { "server": "browser", "tool": "navigate" }
}
]
}
5. internal — 宿主标准能力
{
"name": "list_ports",
"description": "列出串口",
"transport": "internal",
"category": "query",
"internal": { "service": "serialport", "method": "list" }
}
可用 internal 服务:serialport(串口列表/选择)、serialLog(后台串口日志)、state(板卡选项)、arduinoCli(Arduino 编译/烧录/库/板卡)、toolchain(工具链检测状态)。
6. backend — 调用插件自声明的 stdio 后端
{
"name": "config_get",
"description": "读取 Kconfig 符号当前值",
"transport": "backend",
"category": "query",
"backend": { "backendId": "confserver", "method": "get_values", "timeoutMs": 20000 }
}
backend transport 调用插件 backends 里声明的 stdio 长驻后端(JSON-RPC over stdin/stdout),适合「有状态」的工具。
7. mqtt — 通用 MQTT 设备通道
{
"mqttServers": [
{
"name": "printer",
"url": "mqtts://{settings.printer.ip}:8883",
"username": "bblp",
"password": "{settings.printer.accessCode}",
"tls": { "rejectUnauthorized": false },
"topics": ["device/{settings.printer.serial}/report"],
"allowedPublishTopics": ["device/{settings.printer.serial}/request"]
}
],
"tools": [
{
"name": "query_state",
"description": "读取设备最新状态",
"transport": "mqtt",
"category": "query",
"mqtt": { "server": "printer", "op": "read_state", "topic": "device/{settings.printer.serial}/report", "format": "json" }
}
]
}
op 五种:publish(发命令)、request(发命令+等响应)、collect(读消息/等事件)、read_state(最新快照)、status(连接状态)。
allowedPublishTopics 发布白名单 fail-closed;密码用 {settings.*} 占位符存插件设置,永不进日志。能力声明(UI 主按钮)
UI 主按钮(编译 / 上传 / 板卡 / 库管理 / 平台安装)按主动工具链路由到启用插件的类型化能力绑定。能力 id 枚举:
| 能力 | 含义 |
|---|---|
compile | 编译(绑定可声明 output,宿主据此显示编译仪表) |
upload | 烧录 |
flash | 刷写 |
debug | 调试 |
board-manage | 板卡管理 |
board-select | 选择开发板 |
library-manage | 库管理 |
platform-install | 平台安装 |
{
"capabilities": {
"compile": { "transport": "internal", "service": "arduinoCli", "method": "compile", "output": "avr-gcc" },
"upload": { "transport": "internal", "service": "arduinoCli", "method": "upload" }
}
}
每个能力绑定一个工具后端调用(transport + service + method),UI 编译/上传按钮与 AI 工具经同一条能力路由执行。
命令 / 视图 / 项目类型(UI 入口)
插件除工具外,还可声明三种 UI 入口:
命令(commands)
{
"id": "open-about",
"title": "工具链信息",
"icon": "🧛",
"kind": "open-view",
"view": "about",
"placement": "sidebar.board.header"
}
kind:action(触发工具调用,需 transport 绑定)或open-view(打开插件视图)placement:toolbar.action/sidebar.*.header等槽位;同一槽位多个命令聚合为「更多 ▾」溢出菜单when最小 DSL:projectType=arduino、activeToolchain!=x,支持&& / || / !;未知 key 一律隐藏按钮(fail-safe)- 命令 id 由宿主加插件前缀:
{pluginId}.{commandId}
项目类型(projectTypes)
{
"id": "my-firmware",
"title": "我的固件项目",
"icon": "🔧",
"scaffold": [
{ "file": "main.cpp", "content": "// {name} 项目骨架" }
]
}
新建项目页展示插件贡献的项目类型卡片,选中后按 scaffold 模板生成骨架({name} 占位符替换为项目名,宿主做路径安全清洗)。
视图(views,Web 视图)
{
"id": "about",
"title": "工具链信息",
"icon": "🧛",
"slot": "sidebar",
"type": "webview",
"entry": "ui/index.html"
}
slot:sidebar/activitybar/panel;加载插件打包的 HTML(位于ui/目录),走codelab-plugin://{pluginId}/ui/...协议- iframe 沙箱无 same-origin;与宿主通信走 postMessage(宿主校验消息来源与 schema)
- webview 双向通信:
ready/host.ready、host.invoke(调用宿主白名单能力)、state.subscribe(订阅设备上下文)、events(接收宿主事件推送)
设备面板(devicePanel)
活动栏「设备」标签是数据驱动的通用设备壳:插件声明设备面板(选择器 + 配置选项 + 动作 + 可选 webview),宿主渲染通用控件。
{
"devicePanel": {
"id": "arduino-board",
"title": "Arduino 设备",
"selectors": [
{
"id": "board", "title": "开发板",
"source": { "transport": "internal", "service": "arduinoCli", "method": "board.search" },
"searchable": true, "key": "fqbn", "placeholder": "搜索开发板…"
},
{
"id": "port", "title": "端口",
"source": { "transport": "internal", "service": "serialport", "method": "list" },
"key": "port"
}
],
"actions": [
{
"id": "open-monitor", "title": "串口监视器", "kind": "action",
"placement": "device-panel.action",
"transport": "internal", "internal": { "service": "serialLog", "method": "open" }
}
]
}
}
- selectors:设备维度选择器,source 复用工具后端绑定;
searchable开启搜索式下拉;选中结果按 key 写入设备上下文 - actions:设备动作(复用命令机制,placement 限定
device-panel.action),触发时自动携带设备上下文(port / fqbn / device) - 宿主按 selectors 的 key 派生编译/上传前置门控
needsBoard/needsPort:Arduino 有 fqbn+port → 都要先选;ESP-IDF 只有 target+port → 只需端口
工具链检测与一键安装
工具链插件可声明 detection(宿主通用检测引擎)与 installer(一键下载运行官方安装器)。检测结果存 pluginSettings.<pluginId>.toolchain,供 {settings.toolchain.active.*} 占位符使用:
{
"detection": {
"envVars": ["IDF_PATH"],
"installerJson": {
"paths": ["C:/Espressif/idf-env.json", "D:/Espressif/idf-env.json"],
"selectedKey": "idfSelectedId",
"entriesKey": "idfInstalled",
"pathField": "path"
},
"pathGlobs": ["C:/Espressif/frameworks/esp-idf-v*"],
"validateFile": "tools/idf.py"
},
"installer": {
"url": "https://dl.espressif.com/dl/esp-idf/idf-installer.exe",
"fileName": "idf-installer.exe"
}
}
检测源按序:手动路径 → 环境变量(envVars)→ 官方安装器 JSON(installerJson)→ 路径 glob(pathGlobs)→ 校验文件(validateFile)。
依赖与后端
- 依赖下载:
dependencies数组声明(URL 或本地路径,支持 zip 解压),安装时自动下载到plugins/bin/ - 长驻后端:
backends声明随插件启停的服务进程(grpc / process / http / stdio 四型)。grpc/process/http 由宿主 BackendProcessManager 管理生命周期;stdio 由 stdioBridge 惰性管理
打包与发布
插件打包为 zip 上传到市场。zip 内可直接是 manifest.json 或包在一个子目录里。安装方式:插件市场一键安装,或「设置 → 插件 → 从文件夹安装」。