From cea8011f3d0785301a8ffaf1d1e5357fa8516520 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sun, 13 Sep 2026 14:17:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(pluginmgr):=20plugin=5Finstall=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=9C=AC=E6=9C=BA=20path=EF=BC=88=E9=85=8D=E5=90=88?= =?UTF-8?q?=20plugindev=5Fbuild=20=E7=9A=84=E4=BA=A7=E7=89=A9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:Agent 现在能自己构建插件了(plugindev 插件封装了 hmapdev),但安装只支持 http(s) URL —— 本地刚构建出来的 `dist/*.hmap` 装不上,链路断在最后一步。 pluginmgr 的 HTTP API 本来就接受 `{path}`(installFromPath),只是工具面没暴露。 改动:`plugin_install` 增加可选 `path`(本机 .hmap 路径),与 `url` 二选一, 同时给出时以 `path` 为准;`path` 必须存在且不是目录。描述里写明 「配合 plugindev_build 的产物用这个」。 于是 Agent 的完整闭环成立: plugindev_init → plugindev_build → plugin_install(path) → plgreload --- internal/plugins/pluginmgr/plugin.go | 61 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/internal/plugins/pluginmgr/plugin.go b/internal/plugins/pluginmgr/plugin.go index 6ad8ff4..699dfde 100644 --- a/internal/plugins/pluginmgr/plugin.go +++ b/internal/plugins/pluginmgr/plugin.go @@ -138,28 +138,39 @@ func (p *Plugin) Stop() error { func (p *Plugin) registerTools(s *sdk.PluginSDK) { s.RegisterTool("plugin_install", sdk.ToolDef{ Name: "plugin_install", - Description: "从 URL 安装 HomeAgent 插件包(.hmap 文件)。插件已存在时传 overwrite=true 原地更新(升级/降级/重装,保留配置表,无需卸载重装)。更新后需调用 plgreload 或重启生效。", + Description: "安装 HomeAgent 插件包(.hmap)。两种来源:url(http/https 下载)或 path(本机路径,配合 plugindev_build 的产物用这个)。插件已存在时传 overwrite=true 原地更新(升级/降级/重装,保留配置表,无需卸载重装)。更新后需调用 plgreload 或重启生效。", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "url": map[string]interface{}{ "type": "string", - "description": "插件包的下载 URL", + "description": "插件包的下载 URL(http/https)", + }, + "path": map[string]interface{}{ + "type": "string", + "description": "插件包在**本机**的路径(.hmap)。与 url 二选一;同时给出时以 path 为准", }, "overwrite": map[string]interface{}{ "type": "boolean", "description": "已存在时原地更新(保留配置)。默认 false", }, }, - "required": []string{"url"}, }, }, func(args map[string]interface{}) (interface{}, error) { - url, _ := args["url"].(string) - if url == "" { - return map[string]interface{}{"error": "url is required"}, nil - } overwrite, _ := args["overwrite"].(bool) - return p.installFromURL(url, overwrite) + // path 优先:它对应"agent 自己构建出产物再装"的场景(plugindev_build → plugin_install)。 + if path, _ := args["path"].(string); strings.TrimSpace(path) != "" { + pth := strings.TrimSpace(path) + if st, err := os.Stat(pth); err != nil || st.IsDir() { + return map[string]interface{}{"error": fmt.Sprintf("path 无效(必须是存在的 .hmap 文件): %s", pth)}, nil + } + return p.installFromPath(pth, overwrite) + } + url, _ := args["url"].(string) + if strings.TrimSpace(url) == "" { + return map[string]interface{}{"error": "需要 url 或 path(二选一)"}, nil + } + return p.installFromURL(strings.TrimSpace(url), overwrite) }) s.RegisterTool("plugin_list", sdk.ToolDef{ @@ -455,12 +466,12 @@ func (p *Plugin) installFromData(data []byte, overwrite bool) (interface{}, erro if existing && !overwrite { return map[string]interface{}{ - "error": "plugin already exists", - "name": pkg.Name, - "version": pkg.Version, - "current": oldVersion, - "action": "remove_first", - "hint": `传 "overwrite": true 可原地更新(保留配置)`, + "error": "plugin already exists", + "name": pkg.Name, + "version": pkg.Version, + "current": oldVersion, + "action": "remove_first", + "hint": `传 "overwrite": true 可原地更新(保留配置)`, }, nil } @@ -475,7 +486,7 @@ func (p *Plugin) installFromData(data []byte, overwrite bool) (interface{}, erro os.RemoveAll(backup) if err := os.Rename(target, backup); err != nil { return map[string]interface{}{ - "error": "backup old plugin dir failed", + "error": "backup old plugin dir failed", "details": err.Error(), }, nil } @@ -484,8 +495,8 @@ func (p *Plugin) installFromData(data []byte, overwrite bool) (interface{}, erro os.RemoveAll(target) if rbErr := os.Rename(backup, target); rbErr != nil { return map[string]interface{}{ - "error": "extract failed AND rollback failed", - "details": err.Error(), + "error": "extract failed AND rollback failed", + "details": err.Error(), "rollback": rbErr.Error(), }, nil } @@ -507,15 +518,15 @@ func (p *Plugin) installFromData(data []byte, overwrite bool) (interface{}, erro action = "reinstalled" } return map[string]interface{}{ - "status": "installed", - "name": pkg.Name, - "version": pkg.Version, + "status": "installed", + "name": pkg.Name, + "version": pkg.Version, "previous_version": oldVersion, - "entry": pkg.Entry, - "checksum": checksum, - "action": action, - "reload_required": true, - "config_kept": true, + "entry": pkg.Entry, + "checksum": checksum, + "action": action, + "reload_required": true, + "config_kept": true, }, nil }