feat: Phase 3 插件与工具系统 — Plugin SDK + Plugin Manager + 13内置插件 (40文件, 3293行)

- Plugin SDK: Plugin/Tool/ComplexTool/HostAPI 标准化接口
- Plugin Manager: 插件生命周期管理 (Install/Enable/Disable/Uninstall/Reload)
- Tool Registry: 聚合工具注册表 (Register/Execute/Dispatch)
- 13 个内置插件: 将原有硬编码工具迁移为标准插件格式
- REST API: 11 个端点 (net/http, 零外部依赖)
- ai-core 集成: PluginManagerClient 替代本地工具调用
- plugin.json 元数据: 每个插件含完整 author/version/category/permissions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 15:50:19 +08:00
parent 87214b9441
commit 717ad65b05
42 changed files with 3797 additions and 0 deletions
@@ -0,0 +1,189 @@
package iotcontrol
import (
"context"
"fmt"
"github.com/yourname/cyrene-ai/plugin-manager/internal/sdk"
)
// IoTController extends IoTClient with control operations.
type IoTController interface {
GetDevice(ctx context.Context, deviceID string) (*sdk.IoTDeviceState, error)
SetDeviceProperty(ctx context.Context, deviceID, property string, value interface{}) error
ToggleDevice(ctx context.Context, deviceID string) (*sdk.IoTDeviceState, error)
}
type IoTControlPlugin struct {
sdk.BasePlugin
iotClient IoTController
}
func NewIoTControlPlugin(client IoTController) *IoTControlPlugin {
return &IoTControlPlugin{iotClient: client}
}
func (p *IoTControlPlugin) Metadata() sdk.PluginMetadata {
return sdk.PluginMetadata{
Name: "iot_control", DisplayName: "IoT Device Control", Version: "1.0.0",
Description: "Control smart home devices: toggle, set temperature/brightness/mode/color",
Category: "iot", Author: sdk.PluginAuthor{Name: "Cyrene Team"},
}
}
func (p *IoTControlPlugin) Tools() []sdk.Tool {
return []sdk.Tool{&IoTControlTool{iotClient: p.iotClient}}
}
type IoTControlTool struct {
sdk.BaseTool
iotClient IoTController
}
func (t *IoTControlTool) Definition() sdk.ToolDefinition {
return sdk.ToolDefinition{
ID: "iot_control", Name: "iot_control", DisplayName: "IoT Device Control",
Description: "Control smart home devices. Supports toggle, turn_on, turn_off, set_temperature, set_brightness, set_position, set_mode, set_color.",
Category: "iot", Complexity: sdk.ComplexitySimple,
DangerLevel: "medium",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"device_id": map[string]interface{}{"type": "string"},
"action": map[string]interface{}{"type": "string", "enum": []string{"toggle", "turn_on", "turn_off", "set_temperature", "set_brightness", "set_position", "set_mode", "set_color"}},
"value": map[string]interface{}{},
},
"required": []string{"device_id", "action"},
},
}
}
func (t *IoTControlTool) Validate(args map[string]interface{}) error {
for _, k := range []string{"device_id", "action"} {
if _, ok := args[k]; !ok {
return fmt.Errorf("missing required parameter: %s", k)
}
}
return nil
}
func (t *IoTControlTool) Execute(ctx context.Context, args map[string]interface{}) (*sdk.ToolResult, error) {
if t.iotClient == nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: "IoT client not configured"}, nil
}
deviceID, _ := args["device_id"].(string)
if deviceID == "" {
deviceID, _ = args["entity_id"].(string)
}
action := normalizeAction(args)
switch action {
case "turn_on", "turn_off":
status := "on"
if action == "turn_off" {
status = "off"
}
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "status", status); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Device %s turned %s", deviceID, status)}, nil
case "set_temperature":
value := toFloat64(args["value"])
old := ""
if dev, err := t.iotClient.GetDevice(ctx, deviceID); err == nil {
old = fmt.Sprintf(" (was %.1fC)", dev.Temperature)
}
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "temperature", value); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Temperature set to %.1fC%s", value, old)}, nil
case "set_brightness":
value := toFloat64(args["value"])
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "brightness", value); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Brightness set to %.0f%%", value)}, nil
case "set_position":
value := toFloat64(args["value"])
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "position", value); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Position set to %.0f%%", value)}, nil
case "set_mode":
value, _ := args["value"].(string)
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "mode", value); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Mode set to %s", value)}, nil
case "set_color":
value, _ := args["value"].(string)
if err := t.iotClient.SetDeviceProperty(ctx, deviceID, "color", value); err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Color set to %s", value)}, nil
case "toggle":
dev, err := t.iotClient.ToggleDevice(ctx, deviceID)
if err != nil {
return &sdk.ToolResult{ToolName: "iot_control", Success: false, Error: err.Error()}, nil
}
return &sdk.ToolResult{ToolName: "iot_control", Success: true,
Output: fmt.Sprintf("Device %s toggled to %s", deviceID, dev.Status)}, nil
default:
return &sdk.ToolResult{ToolName: "iot_control", Success: false,
Error: fmt.Sprintf("unknown action: %s", action)}, nil
}
}
func normalizeAction(args map[string]interface{}) string {
action, _ := args["action"].(string)
// Chinese aliases
switch action {
case "打开":
return "turn_on"
case "关闭", "关掉", "关上":
return "turn_off"
case "设置温度", "调温度":
return "set_temperature"
case "设置亮度", "调亮度":
return "set_brightness"
case "设置位置":
return "set_position"
case "设置模式":
return "set_mode"
case "设置颜色":
return "set_color"
case "开关", "切换":
return "toggle"
}
return action
}
func toFloat64(v interface{}) float64 {
switch n := v.(type) {
case float64:
return n
case int:
return float64(n)
case int64:
return float64(n)
case string:
var f float64
fmt.Sscanf(n, "%f", &f)
return f
}
return 0
}
@@ -0,0 +1,12 @@
{
"name": "iot_control",
"displayName": "IoT Device Control",
"version": "1.0.0",
"minCyreneVersion": "1.0.0",
"author": { "name": "Cyrene Team" },
"description": "Control smart home devices: toggle, set temperature/brightness/mode/color",
"license": "MIT",
"keywords": ["iot", "control", "toggle", "temperature"],
"category": "iot",
"permissions": ["iot:read", "iot:write"]
}