From 7506e709e239338e6bebdf3604d4e719d27748c4 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:20:55 +0800 Subject: [PATCH] Add plugin management support to OpenClaw. (#13388) --- agent/app/api/v2/agents.go | 82 ++++ agent/app/dto/agents.go | 46 +++ agent/app/service/agents.go | 4 + agent/app/service/agents_plugins.go | 315 ++++++++++++++++ agent/i18n/lang/en.yaml | 5 + agent/i18n/lang/es-ES.yaml | 5 + agent/i18n/lang/fa.yaml | 5 + agent/i18n/lang/ja.yaml | 5 + agent/i18n/lang/ko.yaml | 5 + agent/i18n/lang/lo.yaml | 5 + agent/i18n/lang/ms.yaml | 5 + agent/i18n/lang/pt-BR.yaml | 5 + agent/i18n/lang/ru.yaml | 5 + agent/i18n/lang/tr.yaml | 5 + agent/i18n/lang/zh-Hant.yaml | 5 + agent/i18n/lang/zh.yaml | 5 + agent/router/ro_ai.go | 4 + frontend/src/api/interface/ai.ts | 46 +++ frontend/src/api/modules/ai.ts | 16 + frontend/src/lang/modules/en.ts | 24 ++ frontend/src/lang/modules/es-es.ts | 24 ++ frontend/src/lang/modules/fa.ts | 24 ++ frontend/src/lang/modules/ja.ts | 24 ++ frontend/src/lang/modules/ko.ts | 24 ++ frontend/src/lang/modules/lo.ts | 24 ++ frontend/src/lang/modules/ms.ts | 24 ++ frontend/src/lang/modules/pt-br.ts | 24 ++ frontend/src/lang/modules/ru.ts | 24 ++ frontend/src/lang/modules/tr.ts | 24 ++ frontend/src/lang/modules/zh-Hant.ts | 24 ++ frontend/src/lang/modules/zh.ts | 24 ++ .../views/ai/agents/agent/config/index.vue | 16 + .../ai/agents/agent/config/tabs/plugins.vue | 357 ++++++++++++++++++ 33 files changed, 1234 insertions(+) create mode 100644 agent/app/service/agents_plugins.go create mode 100644 frontend/src/views/ai/agents/agent/config/tabs/plugins.vue diff --git a/agent/app/api/v2/agents.go b/agent/app/api/v2/agents.go index 264c073b0..44d69b200 100644 --- a/agent/app/api/v2/agents.go +++ b/agent/app/api/v2/agents.go @@ -1378,6 +1378,88 @@ func (b *BaseApi) UninstallAgentSkill(c *gin.Context) { helper.Success(c) } +// @Tags AI +// @Summary List OpenClaw plugins +// @Accept json +// @Param request body dto.AgentPluginsReq true "request" +// @Success 200 {array} dto.AgentPluginItem +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/plugins/list [post] +func (b *BaseApi) ListAgentPlugins(c *gin.Context) { + var req dto.AgentPluginsReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + data, err := agentService.ListPlugins(req) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags AI +// @Summary Search OpenClaw plugins +// @Accept json +// @Param request body dto.AgentPluginSearchReq true "request" +// @Success 200 {array} dto.AgentPluginSearchItem +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/plugins/search [post] +func (b *BaseApi) SearchAgentPlugins(c *gin.Context) { + var req dto.AgentPluginSearchReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + data, err := agentService.SearchPlugins(req) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags AI +// @Summary Install an OpenClaw marketplace plugin +// @Accept json +// @Param request body dto.AgentPluginMarketInstallReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/plugins/install [post] +func (b *BaseApi) InstallAgentMarketPlugin(c *gin.Context) { + var req dto.AgentPluginMarketInstallReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.InstallMarketPlugin(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} + +// @Tags AI +// @Summary Operate an OpenClaw plugin +// @Accept json +// @Param request body dto.AgentPluginOperateReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /ai/agents/plugins/operate [post] +func (b *BaseApi) OperateAgentPlugin(c *gin.Context) { + var req dto.AgentPluginOperateReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := agentService.OperatePlugin(req); err != nil { + helper.BadRequest(c, err) + return + } + helper.Success(c) +} + // @Tags AI // @Summary Login Agent Weixin channel // @Accept json diff --git a/agent/app/dto/agents.go b/agent/app/dto/agents.go index f18a91a93..6d2afd869 100644 --- a/agent/app/dto/agents.go +++ b/agent/app/dto/agents.go @@ -604,6 +604,52 @@ type AgentPluginStatus struct { Upgradable bool `json:"upgradable"` } +type AgentPluginsReq struct { + AgentID uint `json:"agentId" validate:"required"` +} + +type AgentPluginSearchReq struct { + AgentID uint `json:"agentId" validate:"required"` + Keyword string `json:"keyword" validate:"required,max=100"` + Limit int `json:"limit" validate:"omitempty,min=1,max=100"` +} + +type AgentPluginMarketInstallReq struct { + AgentID uint `json:"agentId" validate:"required"` + Package string `json:"package" validate:"required,max=200"` + Version string `json:"version" validate:"required,max=100"` + TaskID string `json:"taskID" validate:"required"` +} + +type AgentPluginOperateReq struct { + AgentID uint `json:"agentId" validate:"required"` + PluginID string `json:"pluginId" validate:"required,max=200"` + Operate string `json:"operate" validate:"required,oneof=enable disable update uninstall"` + TaskID string `json:"taskID" validate:"required"` +} + +type AgentPluginItem struct { + ID string `json:"id"` + Name string `json:"name"` + Version string `json:"version"` + Origin string `json:"origin"` + Enabled bool `json:"enabled"` +} + +type AgentPluginSearchItem struct { + Package string `json:"package"` + PluginID string `json:"pluginId"` + Name string `json:"name"` + Description string `json:"description"` + Version string `json:"version"` + Channel string `json:"channel"` + VerificationTier string `json:"verificationTier"` + Categories []string `json:"categories"` + Official bool `json:"official"` + Downloads int64 `json:"downloads"` + Score float64 `json:"score"` +} + type AgentDiscordConfigUpdateReq struct { AgentID uint `json:"agentId" validate:"required"` Enabled bool `json:"enabled"` diff --git a/agent/app/service/agents.go b/agent/app/service/agents.go index 76315a062..4495f31ee 100644 --- a/agent/app/service/agents.go +++ b/agent/app/service/agents.go @@ -105,6 +105,10 @@ type IAgentService interface { UpgradePlugin(req dto.AgentPluginUpgradeReq) error UninstallPlugin(req dto.AgentPluginUninstallReq) error CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error) + ListPlugins(req dto.AgentPluginsReq) ([]dto.AgentPluginItem, error) + SearchPlugins(req dto.AgentPluginSearchReq) ([]dto.AgentPluginSearchItem, error) + InstallMarketPlugin(req dto.AgentPluginMarketInstallReq) error + OperatePlugin(req dto.AgentPluginOperateReq) error ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error } diff --git a/agent/app/service/agents_plugins.go b/agent/app/service/agents_plugins.go new file mode 100644 index 000000000..a80bae42d --- /dev/null +++ b/agent/app/service/agents_plugins.go @@ -0,0 +1,315 @@ +package service + +import ( + "database/sql" + "encoding/json" + "fmt" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/task" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/i18n" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/compose" +) + +var ( + openclawPluginPackagePattern = regexp.MustCompile(`^(@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*$`) + openclawPluginVersionPattern = regexp.MustCompile(`^[0-9A-Za-z][0-9A-Za-z._-]*$`) + openclawPluginIDPattern = regexp.MustCompile(`^(@[A-Za-z0-9][A-Za-z0-9._-]*/)?[A-Za-z0-9][A-Za-z0-9._-]*$`) +) + +type openclawPluginListOutput struct { + Plugins []struct { + ID string `json:"id"` + Name string `json:"name"` + Version string `json:"version"` + Origin string `json:"origin"` + Enabled bool `json:"enabled"` + } `json:"plugins"` +} + +type openclawPluginIndexItem struct { + PluginID string `json:"pluginId"` + PackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + Origin string `json:"origin"` + Enabled bool `json:"enabled"` +} + +type openclawPluginSearchOutput struct { + Results []struct { + Score float64 `json:"score"` + Package struct { + Name string `json:"name"` + RuntimeID string `json:"runtimeId"` + DisplayName string `json:"displayName"` + Summary string `json:"summary"` + LatestVersion string `json:"latestVersion"` + Categories []string `json:"categories"` + Channel string `json:"channel"` + IsOfficial bool `json:"isOfficial"` + VerificationTier string `json:"verificationTier"` + Stats struct { + Downloads int64 `json:"downloads"` + } `json:"stats"` + } `json:"package"` + } `json:"results"` +} + +func (a AgentService) ListPlugins(req dto.AgentPluginsReq) ([]dto.AgentPluginItem, error) { + agent, install, err := a.loadOpenclawAgentAndInstall(req.AgentID) + if err != nil { + return nil, err + } + if plugins, err := readOpenclawPluginIndex(filepath.Join(filepath.Dir(agent.ConfigPath), "state", "openclaw.sqlite")); err == nil { + return plugins, nil + } + output, err := cmd.RunDockerExecWithStdout(2*time.Minute, install.ContainerName, "openclaw", "plugins", "list", "--json") + if err != nil { + return nil, err + } + return parseOpenclawPluginList([]byte(output)) +} + +func (a AgentService) SearchPlugins(req dto.AgentPluginSearchReq) ([]dto.AgentPluginSearchItem, error) { + _, install, err := a.loadOpenclawAgentAndInstall(req.AgentID) + if err != nil { + return nil, err + } + limit := req.Limit + if limit == 0 { + limit = 20 + } + output, err := cmd.RunDockerExecWithStdout( + 2*time.Minute, + install.ContainerName, + "openclaw", "plugins", "search", strings.TrimSpace(req.Keyword), "--limit", fmt.Sprint(limit), "--json", + ) + if err != nil { + return nil, err + } + return parseOpenclawPluginSearch([]byte(output)) +} + +func (a AgentService) InstallMarketPlugin(req dto.AgentPluginMarketInstallReq) error { + spec, err := buildOpenclawPluginInstallSpec(req.Package, req.Version) + if err != nil { + return err + } + _, install, err := a.loadOpenclawAgentAndInstall(req.AgentID) + if err != nil { + return err + } + if err := task.CheckScopeTaskIsExecuting(task.TaskScopeAI, req.AgentID); err != nil { + return err + } + taskName := fmt.Sprintf("%s [%s]", i18n.GetMsgByKey("AgentPluginInstall"), req.Package) + installTask, err := task.NewTask(taskName, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID) + if err != nil { + return err + } + installTask.AddSubTask(taskName, func(t *task.Task) error { + mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute)) + return mgr.Run("docker", "exec", install.ContainerName, "openclaw", "plugins", "install", spec) + }, nil) + addOpenclawPluginRestartTask(installTask, install) + go executeAgentPluginTask(installTask) + return nil +} + +func (a AgentService) OperatePlugin(req dto.AgentPluginOperateReq) error { + if !openclawPluginIDPattern.MatchString(req.PluginID) { + return buserr.New("ErrInvalidChar") + } + agent, install, err := a.loadOpenclawAgentAndInstall(req.AgentID) + if err != nil { + return err + } + if err := task.CheckScopeTaskIsExecuting(task.TaskScopeAI, req.AgentID); err != nil { + return err + } + if req.Operate == "update" || req.Operate == "uninstall" { + plugins, err := a.ListPlugins(dto.AgentPluginsReq{AgentID: req.AgentID}) + if err != nil { + return err + } + for _, plugin := range plugins { + if plugin.ID == req.PluginID && plugin.Origin == "bundled" { + return buserr.WithName("ErrNotSupportType", req.Operate) + } + } + } + + taskType := map[string]string{ + "enable": task.TaskUpdate, + "disable": task.TaskUpdate, + "update": task.TaskUpgrade, + "uninstall": task.TaskUninstall, + }[req.Operate] + taskName := fmt.Sprintf("%s [%s]", i18n.GetMsgByKey(map[string]string{ + "enable": "AgentPluginEnable", + "disable": "AgentPluginDisable", + "update": "AgentPluginUpdate", + "uninstall": "AgentPluginUninstall", + }[req.Operate]), req.PluginID) + operateTask, err := task.NewTask(taskName, taskType, task.TaskScopeAI, req.TaskID, req.AgentID) + if err != nil { + return err + } + operateTask.AddSubTask(taskName, func(t *task.Task) error { + mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute)) + if req.Operate == "uninstall" { + if err := uninstallOpenclawPlugin(mgr, install.ContainerName, req.PluginID); err != nil { + return err + } + return cleanupManagedOpenclawPlugin(agent, req.PluginID) + } + return mgr.Run("docker", "exec", install.ContainerName, "openclaw", "plugins", req.Operate, req.PluginID) + }, nil) + addOpenclawPluginRestartTask(operateTask, install) + go executeAgentPluginTask(operateTask) + return nil +} + +func parseOpenclawPluginList(raw []byte) ([]dto.AgentPluginItem, error) { + payload, err := extractEmbeddedJSON(string(raw)) + if err != nil { + return nil, err + } + if len(payload) == 0 { + return []dto.AgentPluginItem{}, nil + } + var output openclawPluginListOutput + if err := json.Unmarshal(payload, &output); err != nil { + return nil, err + } + items := make([]dto.AgentPluginItem, 0, len(output.Plugins)) + for _, plugin := range output.Plugins { + items = append(items, dto.AgentPluginItem{ + ID: plugin.ID, + Name: plugin.Name, + Version: plugin.Version, + Origin: plugin.Origin, + Enabled: plugin.Enabled, + }) + } + return items, nil +} + +func readOpenclawPluginIndex(dbPath string) ([]dto.AgentPluginItem, error) { + db, err := sql.Open("sqlite", "file:"+filepath.ToSlash(dbPath)+"?mode=ro") + if err != nil { + return nil, err + } + defer db.Close() + + var raw []byte + if err := db.QueryRow( + "SELECT plugins_json FROM installed_plugin_index WHERE index_key = ?", + "installed-plugin-index", + ).Scan(&raw); err != nil { + return nil, err + } + + var plugins []openclawPluginIndexItem + if err := json.Unmarshal(raw, &plugins); err != nil { + return nil, err + } + items := make([]dto.AgentPluginItem, 0, len(plugins)) + for _, plugin := range plugins { + name := plugin.PackageName + if name == "" { + name = plugin.PluginID + } + items = append(items, dto.AgentPluginItem{ + ID: plugin.PluginID, + Name: name, + Version: plugin.PackageVersion, + Origin: plugin.Origin, + Enabled: plugin.Enabled, + }) + } + return items, nil +} + +func parseOpenclawPluginSearch(raw []byte) ([]dto.AgentPluginSearchItem, error) { + payload, err := extractEmbeddedJSON(string(raw)) + if err != nil { + return nil, err + } + if len(payload) == 0 { + return []dto.AgentPluginSearchItem{}, nil + } + var output openclawPluginSearchOutput + if err := json.Unmarshal(payload, &output); err != nil { + return nil, err + } + items := make([]dto.AgentPluginSearchItem, 0, len(output.Results)) + for _, result := range output.Results { + items = append(items, dto.AgentPluginSearchItem{ + Package: result.Package.Name, + PluginID: result.Package.RuntimeID, + Name: result.Package.DisplayName, + Description: result.Package.Summary, + Version: result.Package.LatestVersion, + Channel: result.Package.Channel, + VerificationTier: result.Package.VerificationTier, + Categories: append([]string{}, result.Package.Categories...), + Official: result.Package.IsOfficial, + Downloads: result.Package.Stats.Downloads, + Score: result.Score, + }) + } + return items, nil +} + +func buildOpenclawPluginInstallSpec(packageName, version string) (string, error) { + packageName = strings.TrimSpace(packageName) + version = strings.TrimSpace(version) + if !openclawPluginPackagePattern.MatchString(packageName) || !openclawPluginVersionPattern.MatchString(version) { + return "", buserr.New("ErrInvalidChar") + } + return "clawhub:" + packageName + "@" + version, nil +} + +func cleanupManagedOpenclawPlugin(agent *model.Agent, pluginID string) error { + pluginType := map[string]string{ + "openclaw-lark": "feishu", + "openclaw-qqbot": "qqbot", + "wecom-openclaw-plugin": "wecom", + "dingtalk-connector": "dingtalk", + "openclaw-weixin": "weixin", + }[pluginID] + if pluginType == "" { + return nil + } + conf, err := readOpenclawConfig(agent.ConfigPath) + if err != nil { + return err + } + cleanupOpenclawPluginConfig(conf, pluginType) + return writeOpenclawConfigRaw(agent.ConfigPath, conf) +} + +func addOpenclawPluginRestartTask(t *task.Task, install *model.AppInstall) { + t.AddSubTask(task.GetTaskName("OpenClaw", task.TaskRestart, task.TaskScopeAI), func(t *task.Task) error { + output, err := compose.Restart(install.GetComposePath()) + if output != "" { + t.Log(output) + } + return err + }, nil) +} + +func executeAgentPluginTask(t *task.Task) { + if err := t.Execute(); err != nil { + global.LOG.Errorf("operate openclaw plugin failed: %v", err) + } +} diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 0722d10b8..c1f5b1bd1 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Create' TaskDelete: 'Delete' TaskUpgrade: 'Upgrade' TaskUpdate: 'Update' +AgentPluginInstall: 'Install plugin' +AgentPluginEnable: 'Enable plugin' +AgentPluginDisable: 'Disable plugin' +AgentPluginUpdate: 'Update plugin' +AgentPluginUninstall: 'Uninstall plugin' TaskRestart: 'Restart' TaskProtect: "Protect" TaskBackup: 'Backup' diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index b30ed5979..6b68ba3f5 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Crear' TaskDelete: 'Eliminar' TaskUpgrade: 'Actualizar' TaskUpdate: 'Actualizar' +AgentPluginInstall: 'Instalar complemento' +AgentPluginEnable: 'Activar complemento' +AgentPluginDisable: 'Desactivar complemento' +AgentPluginUpdate: 'Actualizar complemento' +AgentPluginUninstall: 'Desinstalar complemento' TaskRestart: 'Reiniciar' TaskProtect: 'Proteger' TaskBackup: 'Respaldar' diff --git a/agent/i18n/lang/fa.yaml b/agent/i18n/lang/fa.yaml index 73b5b2235..c61e8bca4 100644 --- a/agent/i18n/lang/fa.yaml +++ b/agent/i18n/lang/fa.yaml @@ -449,6 +449,11 @@ TaskCreate: 'ایجاد' TaskDelete: 'حذف' TaskUpgrade: 'ارتقاء' TaskUpdate: 'به‌روزرسانی' +AgentPluginInstall: 'نصب افزونه' +AgentPluginEnable: 'فعال‌کردن افزونه' +AgentPluginDisable: 'غیرفعال‌کردن افزونه' +AgentPluginUpdate: 'به‌روزرسانی افزونه' +AgentPluginUninstall: 'حذف افزونه' TaskRestart: 'راه‌اندازی مجدد' TaskProtect: "محافظت" TaskBackup: 'پشتیبان‌گیری' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index 4f16d4cca..98d80432d 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -449,6 +449,11 @@ TaskCreate: '作成' TaskDelete: '削除' TaskUpgrade: 'アップグレード' TaskUpdate: '更新' +AgentPluginInstall: 'プラグインをインストール' +AgentPluginEnable: 'プラグインを有効化' +AgentPluginDisable: 'プラグインを無効化' +AgentPluginUpdate: 'プラグインを更新' +AgentPluginUninstall: 'プラグインをアンインストール' TaskRestart: '再起動' TaskProtect: '保護' TaskBackup: 'バックアップ' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index e19034683..17a222561 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -449,6 +449,11 @@ TaskCreate: '생성' TaskDelete: '삭제' TaskUpgrade: '업그레이드' TaskUpdate: '업데이트' +AgentPluginInstall: '플러그인 설치' +AgentPluginEnable: '플러그인 활성화' +AgentPluginDisable: '플러그인 비활성화' +AgentPluginUpdate: '플러그인 업데이트' +AgentPluginUninstall: '플러그인 제거' TaskRestart: '다시 시작' TaskProtect: '보호' TaskBackup: '백업' diff --git a/agent/i18n/lang/lo.yaml b/agent/i18n/lang/lo.yaml index 27a8aeb9d..f75442dd7 100644 --- a/agent/i18n/lang/lo.yaml +++ b/agent/i18n/lang/lo.yaml @@ -439,6 +439,11 @@ TaskCreate: 'ສ້າງ' TaskDelete: 'ລຶບ' TaskUpgrade: 'ອັບເກຣດ' TaskUpdate: 'ອັບເດດ' +AgentPluginInstall: 'ຕິດຕັ້ງປລັກອິນ' +AgentPluginEnable: 'ເປີດໃຊ້ປລັກອິນ' +AgentPluginDisable: 'ປິດໃຊ້ປລັກອິນ' +AgentPluginUpdate: 'ອັບເດດປລັກອິນ' +AgentPluginUninstall: 'ຖອນການຕິດຕັ້ງປລັກອິນ' TaskRestart: 'ເລີ່ມໃໝ່' TaskProtect: "ປ້ອງກັນ" TaskBackup: 'ສຳຮອງຂໍ້ມູນ' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index 4bfb7fd68..7e58549ac 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Buat' TaskDelete: 'Padam' TaskUpgrade: 'Naik taraf' TaskUpdate: 'Kemas kini' +AgentPluginInstall: 'Pasang pemalam' +AgentPluginEnable: 'Aktifkan pemalam' +AgentPluginDisable: 'Nyahaktifkan pemalam' +AgentPluginUpdate: 'Kemas kini pemalam' +AgentPluginUninstall: 'Nyahpasang pemalam' TaskRestart: 'Mulakan semula' TaskProtect: 'Lindungi' TaskBackup: 'Sandaran' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 222305822..76b645373 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Criar' TaskDelete: 'Excluir' TaskUpgrade: 'Atualizar' TaskUpdate: 'Atualizar' +AgentPluginInstall: 'Instalar plugin' +AgentPluginEnable: 'Ativar plugin' +AgentPluginDisable: 'Desativar plugin' +AgentPluginUpdate: 'Atualizar plugin' +AgentPluginUninstall: 'Desinstalar plugin' TaskRestart: 'Reiniciar' TaskProtect: 'Proteger' TaskBackup: 'Backup' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index 5f90328d9..b24ec2617 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Создать' TaskDelete: 'Удалить' TaskUpgrade: 'Обновить' TaskUpdate: 'Обновить' +AgentPluginInstall: 'Установить плагин' +AgentPluginEnable: 'Включить плагин' +AgentPluginDisable: 'Отключить плагин' +AgentPluginUpdate: 'Обновить плагин' +AgentPluginUninstall: 'Удалить плагин' TaskRestart: 'Перезапуск' TaskProtect: 'Защита' TaskBackup: 'Резервное копирование' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index 6d17f0a5c..f1b75cbad 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -449,6 +449,11 @@ TaskCreate: 'Oluştur' TaskDelete: 'Sil' TaskUpgrade: 'Yükselt' TaskUpdate: 'Güncelle' +AgentPluginInstall: 'Eklenti yükle' +AgentPluginEnable: 'Eklentiyi etkinleştir' +AgentPluginDisable: 'Eklentiyi devre dışı bırak' +AgentPluginUpdate: 'Eklentiyi güncelle' +AgentPluginUninstall: 'Eklentiyi kaldır' TaskRestart: 'Yeniden Başlat' TaskProtect: 'Koru' TaskBackup: 'Yedekle' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 72e0433b4..82f97470e 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -449,6 +449,11 @@ TaskCreate: '建立' TaskDelete: '刪除' TaskUpgrade: '升級' TaskUpdate: '更新' +AgentPluginInstall: '安裝外掛程式' +AgentPluginEnable: '啟用外掛程式' +AgentPluginDisable: '停用外掛程式' +AgentPluginUpdate: '更新外掛程式' +AgentPluginUninstall: '解除安裝外掛程式' TaskRestart: '重新啟動' TaskProtect: '防護' TaskBackup: '備份' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 2dcc5993f..3030df83e 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -449,6 +449,11 @@ TaskCreate: "创建" TaskDelete: "删除" TaskUpgrade: "升级" TaskUpdate: "更新" +AgentPluginInstall: "安装插件" +AgentPluginEnable: "启用插件" +AgentPluginDisable: "禁用插件" +AgentPluginUpdate: "更新插件" +AgentPluginUninstall: "卸载插件" TaskRestart: "重启" TaskProtect: "防护" TaskBackup: "备份" diff --git a/agent/router/ro_ai.go b/agent/router/ro_ai.go index d7934a17e..dd8ce2b95 100644 --- a/agent/router/ro_ai.go +++ b/agent/router/ro_ai.go @@ -104,6 +104,10 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) { aiToolsRouter.POST("/agents/plugin/upgrade", baseApi.UpgradeAgentPlugin) aiToolsRouter.POST("/agents/plugin/uninstall", baseApi.UninstallAgentPlugin) aiToolsRouter.POST("/agents/plugin/check", baseApi.CheckAgentPlugin) + aiToolsRouter.POST("/agents/plugins/list", baseApi.ListAgentPlugins) + aiToolsRouter.POST("/agents/plugins/search", baseApi.SearchAgentPlugins) + aiToolsRouter.POST("/agents/plugins/install", baseApi.InstallAgentMarketPlugin) + aiToolsRouter.POST("/agents/plugins/operate", baseApi.OperateAgentPlugin) aiToolsRouter.POST("/agents/security/get", baseApi.GetAgentSecurityConfig) aiToolsRouter.POST("/agents/security/update", baseApi.UpdateAgentSecurityConfig) aiToolsRouter.POST("/agents/other/get", baseApi.GetAgentOtherConfig) diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index 8787aecf6..4b44d040a 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -853,6 +853,52 @@ export namespace AI { upgradable: boolean; } + export interface AgentPluginsReq { + agentId: number; + } + + export interface AgentPluginSearchReq { + agentId: number; + keyword: string; + limit?: number; + } + + export interface AgentPluginMarketInstallReq { + agentId: number; + package: string; + version: string; + taskID: string; + } + + export interface AgentPluginOperateReq { + agentId: number; + pluginId: string; + operate: 'enable' | 'disable' | 'update' | 'uninstall'; + taskID: string; + } + + export interface AgentPluginItem { + id: string; + name: string; + version: string; + origin: string; + enabled: boolean; + } + + export interface AgentPluginSearchItem { + package: string; + pluginId: string; + name: string; + description: string; + version: string; + channel: string; + verificationTier: string; + categories: string[]; + official: boolean; + downloads: number; + score: number; + } + export interface AgentDiscordConfigReq { agentId: number; } diff --git a/frontend/src/api/modules/ai.ts b/frontend/src/api/modules/ai.ts index e631b021c..40459250f 100644 --- a/frontend/src/api/modules/ai.ts +++ b/frontend/src/api/modules/ai.ts @@ -330,6 +330,22 @@ export const checkAgentPlugin = (req: AI.AgentPluginCheckReq) => { return http.post(`/ai/agents/plugin/check`, req); }; +export const listAgentPlugins = (req: AI.AgentPluginsReq) => { + return http.post(`/ai/agents/plugins/list`, req); +}; + +export const searchAgentPlugins = (req: AI.AgentPluginSearchReq) => { + return http.post(`/ai/agents/plugins/search`, req); +}; + +export const installAgentMarketPlugin = (req: AI.AgentPluginMarketInstallReq) => { + return http.post(`/ai/agents/plugins/install`, req); +}; + +export const operateAgentPlugin = (req: AI.AgentPluginOperateReq) => { + return http.post(`/ai/agents/plugins/operate`, req); +}; + export const getAgentSecurityConfig = (req: AI.AgentSecurityConfigReq) => { return http.post(`/ai/agents/security/get`, req); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 1f54d6329..2ba01541f 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -753,6 +753,30 @@ const message = { verified: 'Verified', verifySkipped: 'No verification', skillsTab: 'Skills', + pluginsTab: 'Plugins', + pluginsInstalled: 'Installed', + pluginsMarket: 'Plugin Market', + pluginsMarketHint: 'Enter a keyword to search for plugins', + pluginSearchPlaceholder: 'Search plugins...', + pluginOrigin: 'Source', + pluginOriginAll: 'All Sources', + pluginOriginBundled: 'Bundled', + pluginOriginExternal: 'External', + pluginStatusAll: 'All Statuses', + pluginEnabled: 'Enabled', + pluginDisabled: 'Disabled', + pluginCategory: 'Category', + pluginVerification: 'Verification', + pluginOfficial: 'Official', + pluginCommunity: 'Community', + pluginDownloads: 'Downloads', + pluginEnable: 'Enable', + pluginDisable: 'Disable', + pluginListEmpty: 'No plugins', + pluginSearchEmpty: 'No matching plugins found', + pluginInstallConfirm: 'Install plugin {0}?', + pluginDisableConfirm: 'Disable plugin {0}?', + pluginUninstallConfirm: 'Uninstall plugin {0}?', securityTab: 'Security', otherTab: 'Other', timeZone: 'Time Zone', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 255129965..572d7401d 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -754,6 +754,30 @@ const message = { verified: 'Verificado', verifySkipped: 'Sin verificación', skillsTab: 'Habilidades', + pluginsTab: 'Complementos', + pluginsInstalled: 'Instalados', + pluginsMarket: 'Mercado de complementos', + pluginsMarketHint: 'Introduce una palabra clave para buscar complementos', + pluginSearchPlaceholder: 'Buscar complementos...', + pluginOrigin: 'Origen', + pluginOriginAll: 'Todos los orígenes', + pluginOriginBundled: 'Integrado', + pluginOriginExternal: 'Externo', + pluginStatusAll: 'Todos los estados', + pluginEnabled: 'Activado', + pluginDisabled: 'Desactivado', + pluginCategory: 'Categoría', + pluginVerification: 'Verificación', + pluginOfficial: 'Oficial', + pluginCommunity: 'Comunidad', + pluginDownloads: 'Descargas', + pluginEnable: 'Activar', + pluginDisable: 'Desactivar', + pluginListEmpty: 'No hay complementos', + pluginSearchEmpty: 'No se encontraron complementos coincidentes', + pluginInstallConfirm: '¿Instalar el complemento {0}?', + pluginDisableConfirm: '¿Desactivar el complemento {0}?', + pluginUninstallConfirm: '¿Desinstalar el complemento {0}?', securityTab: 'Seguridad', otherTab: 'Otros', timeZone: 'Zona horaria', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index 63d0837a7..b099c6c43 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -741,6 +741,30 @@ const message = { verified: 'تأیید شده', verifySkipped: 'بدون تأیید', skillsTab: 'مهارت‌ها', + pluginsTab: 'افزونه‌ها', + pluginsInstalled: 'نصب‌شده', + pluginsMarket: 'بازار افزونه‌ها', + pluginsMarketHint: 'برای جستجوی افزونه یک کلیدواژه وارد کنید', + pluginSearchPlaceholder: 'جستجوی افزونه‌ها...', + pluginOrigin: 'منبع', + pluginOriginAll: 'همه منابع', + pluginOriginBundled: 'داخلی', + pluginOriginExternal: 'خارجی', + pluginStatusAll: 'همه وضعیت‌ها', + pluginEnabled: 'فعال', + pluginDisabled: 'غیرفعال', + pluginCategory: 'دسته‌بندی', + pluginVerification: 'اعتبارسنجی', + pluginOfficial: 'رسمی', + pluginCommunity: 'جامعه', + pluginDownloads: 'دانلودها', + pluginEnable: 'فعال‌کردن', + pluginDisable: 'غیرفعال‌کردن', + pluginListEmpty: 'افزونه‌ای وجود ندارد', + pluginSearchEmpty: 'افزونه مرتبطی پیدا نشد', + pluginInstallConfirm: 'افزونه {0} نصب شود؟', + pluginDisableConfirm: 'افزونه {0} غیرفعال شود؟', + pluginUninstallConfirm: 'افزونه {0} حذف شود؟', securityTab: 'امنیت', otherTab: 'سایر', timeZone: 'منطقه زمانی', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 1284430e9..6078c12b6 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -747,6 +747,30 @@ const message = { verified: '検証済み', verifySkipped: '検証なし', skillsTab: '技能', + pluginsTab: 'プラグイン', + pluginsInstalled: 'インストール済み', + pluginsMarket: 'プラグイン市場', + pluginsMarketHint: 'キーワードを入力してプラグインを検索してください', + pluginSearchPlaceholder: 'プラグインを検索...', + pluginOrigin: '提供元', + pluginOriginAll: 'すべての提供元', + pluginOriginBundled: '組み込み', + pluginOriginExternal: '外部', + pluginStatusAll: 'すべての状態', + pluginEnabled: '有効', + pluginDisabled: '無効', + pluginCategory: 'カテゴリ', + pluginVerification: '検証', + pluginOfficial: '公式', + pluginCommunity: 'コミュニティ', + pluginDownloads: 'ダウンロード数', + pluginEnable: '有効化', + pluginDisable: '無効化', + pluginListEmpty: 'プラグインがありません', + pluginSearchEmpty: '一致するプラグインが見つかりません', + pluginInstallConfirm: 'プラグイン {0} をインストールしますか?', + pluginDisableConfirm: 'プラグイン {0} を無効にしますか?', + pluginUninstallConfirm: 'プラグイン {0} をアンインストールしますか?', securityTab: 'セキュリティ', otherTab: 'その他', timeZone: 'タイムゾーン', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index aae78e8d3..019298cf1 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -738,6 +738,30 @@ const message = { verified: '검증됨', verifySkipped: '검증 안 함', skillsTab: '기술', + pluginsTab: '플러그인', + pluginsInstalled: '설치됨', + pluginsMarket: '플러그인 마켓', + pluginsMarketHint: '키워드를 입력하여 플러그인을 검색하세요', + pluginSearchPlaceholder: '플러그인 검색...', + pluginOrigin: '출처', + pluginOriginAll: '모든 출처', + pluginOriginBundled: '내장', + pluginOriginExternal: '외부', + pluginStatusAll: '모든 상태', + pluginEnabled: '활성화됨', + pluginDisabled: '비활성화됨', + pluginCategory: '분류', + pluginVerification: '검증', + pluginOfficial: '공식', + pluginCommunity: '커뮤니티', + pluginDownloads: '다운로드', + pluginEnable: '활성화', + pluginDisable: '비활성화', + pluginListEmpty: '플러그인이 없습니다', + pluginSearchEmpty: '일치하는 플러그인을 찾을 수 없습니다', + pluginInstallConfirm: '플러그인 {0}을(를) 설치하시겠습니까?', + pluginDisableConfirm: '플러그인 {0}을(를) 비활성화하시겠습니까?', + pluginUninstallConfirm: '플러그인 {0}을(를) 제거하시겠습니까?', securityTab: '보안', otherTab: '기타', timeZone: '시간대', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index a475b41dc..c73a2ba08 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -747,6 +747,30 @@ const message = { verified: 'ກວດສອບແລ້ວ', verifySkipped: 'ບໍ່ມີການກວດສອບ', skillsTab: 'ທັກສະ', + pluginsTab: 'ປລັກອິນ', + pluginsInstalled: 'ຕິດຕັ້ງແລ້ວ', + pluginsMarket: 'ຕະຫຼາດປລັກອິນ', + pluginsMarketHint: 'ປ້ອນຄຳຄົ້ນເພື່ອຊອກຫາປລັກອິນ', + pluginSearchPlaceholder: 'ຊອກຫາປລັກອິນ...', + pluginOrigin: 'ແຫຼ່ງທີ່ມາ', + pluginOriginAll: 'ທຸກແຫຼ່ງທີ່ມາ', + pluginOriginBundled: 'ມາພ້ອມລະບົບ', + pluginOriginExternal: 'ພາຍນອກ', + pluginStatusAll: 'ທຸກສະຖານະ', + pluginEnabled: 'ເປີດໃຊ້ແລ້ວ', + pluginDisabled: 'ປິດໃຊ້ແລ້ວ', + pluginCategory: 'ໝວດໝູ່', + pluginVerification: 'ການຢືນຢັນ', + pluginOfficial: 'ທາງການ', + pluginCommunity: 'ຊຸມຊົນ', + pluginDownloads: 'ດາວໂຫຼດ', + pluginEnable: 'ເປີດໃຊ້', + pluginDisable: 'ປິດໃຊ້', + pluginListEmpty: 'ບໍ່ມີປລັກອິນ', + pluginSearchEmpty: 'ບໍ່ພົບປລັກອິນທີ່ກົງກັນ', + pluginInstallConfirm: 'ຢືນຢັນຕິດຕັ້ງປລັກອິນ {0} ບໍ?', + pluginDisableConfirm: 'ຢືນຢັນປິດໃຊ້ປລັກອິນ {0} ບໍ?', + pluginUninstallConfirm: 'ຢືນຢັນຖອນການຕິດຕັ້ງປລັກອິນ {0} ບໍ?', securityTab: 'ຄວາມປອດໄພ', otherTab: 'ອື່ນໆ', timeZone: 'ເຂດເວລາ', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index eb935c947..2c71c7156 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -755,6 +755,30 @@ const message = { verified: 'Disahkan', verifySkipped: 'Tanpa pengesahan', skillsTab: 'Kemahiran', + pluginsTab: 'Pemalam', + pluginsInstalled: 'Dipasang', + pluginsMarket: 'Pasaran pemalam', + pluginsMarketHint: 'Masukkan kata kunci untuk mencari pemalam', + pluginSearchPlaceholder: 'Cari pemalam...', + pluginOrigin: 'Sumber', + pluginOriginAll: 'Semua sumber', + pluginOriginBundled: 'Terbina dalam', + pluginOriginExternal: 'Luaran', + pluginStatusAll: 'Semua status', + pluginEnabled: 'Diaktifkan', + pluginDisabled: 'Dinyahaktifkan', + pluginCategory: 'Kategori', + pluginVerification: 'Pengesahan', + pluginOfficial: 'Rasmi', + pluginCommunity: 'Komuniti', + pluginDownloads: 'Muat turun', + pluginEnable: 'Aktifkan', + pluginDisable: 'Nyahaktifkan', + pluginListEmpty: 'Tiada pemalam', + pluginSearchEmpty: 'Tiada pemalam sepadan ditemui', + pluginInstallConfirm: 'Pasang pemalam {0}?', + pluginDisableConfirm: 'Nyahaktifkan pemalam {0}?', + pluginUninstallConfirm: 'Nyahpasang pemalam {0}?', securityTab: 'Keselamatan', otherTab: 'Lain-lain', timeZone: 'Zon Waktu', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index d8c9fadd1..e5d9141bb 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -749,6 +749,30 @@ const message = { verified: 'Verificado', verifySkipped: 'Sem verificação', skillsTab: 'Habilidades', + pluginsTab: 'Plugins', + pluginsInstalled: 'Instalados', + pluginsMarket: 'Mercado de plugins', + pluginsMarketHint: 'Digite uma palavra-chave para pesquisar plugins', + pluginSearchPlaceholder: 'Pesquisar plugins...', + pluginOrigin: 'Origem', + pluginOriginAll: 'Todas as origens', + pluginOriginBundled: 'Integrado', + pluginOriginExternal: 'Externo', + pluginStatusAll: 'Todos os estados', + pluginEnabled: 'Ativado', + pluginDisabled: 'Desativado', + pluginCategory: 'Categoria', + pluginVerification: 'Verificação', + pluginOfficial: 'Oficial', + pluginCommunity: 'Comunidade', + pluginDownloads: 'Downloads', + pluginEnable: 'Ativar', + pluginDisable: 'Desativar', + pluginListEmpty: 'Nenhum plugin', + pluginSearchEmpty: 'Nenhum plugin correspondente encontrado', + pluginInstallConfirm: 'Instalar o plugin {0}?', + pluginDisableConfirm: 'Desativar o plugin {0}?', + pluginUninstallConfirm: 'Desinstalar o plugin {0}?', securityTab: 'Segurança', otherTab: 'Outros', timeZone: 'Fuso horário', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 94d8d0038..34df25f1b 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -745,6 +745,30 @@ const message = { verified: 'Проверено', verifySkipped: 'Без проверки', skillsTab: 'Навыки', + pluginsTab: 'Плагины', + pluginsInstalled: 'Установленные', + pluginsMarket: 'Каталог плагинов', + pluginsMarketHint: 'Введите ключевое слово для поиска плагинов', + pluginSearchPlaceholder: 'Поиск плагинов...', + pluginOrigin: 'Источник', + pluginOriginAll: 'Все источники', + pluginOriginBundled: 'Встроенный', + pluginOriginExternal: 'Внешний', + pluginStatusAll: 'Все статусы', + pluginEnabled: 'Включён', + pluginDisabled: 'Отключён', + pluginCategory: 'Категория', + pluginVerification: 'Проверка', + pluginOfficial: 'Официальный', + pluginCommunity: 'Сообщество', + pluginDownloads: 'Загрузки', + pluginEnable: 'Включить', + pluginDisable: 'Отключить', + pluginListEmpty: 'Плагины отсутствуют', + pluginSearchEmpty: 'Подходящие плагины не найдены', + pluginInstallConfirm: 'Установить плагин {0}?', + pluginDisableConfirm: 'Отключить плагин {0}?', + pluginUninstallConfirm: 'Удалить плагин {0}?', securityTab: 'Безопасность', otherTab: 'Другое', timeZone: 'Часовой пояс', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 3fed8ad2b..06f346d12 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -751,6 +751,30 @@ const message = { verified: 'Doğrulandı', verifySkipped: 'Doğrulama yok', skillsTab: 'Yetenekler', + pluginsTab: 'Eklentiler', + pluginsInstalled: 'Yüklü', + pluginsMarket: 'Eklenti mağazası', + pluginsMarketHint: 'Eklenti aramak için bir anahtar kelime girin', + pluginSearchPlaceholder: 'Eklenti ara...', + pluginOrigin: 'Kaynak', + pluginOriginAll: 'Tüm kaynaklar', + pluginOriginBundled: 'Yerleşik', + pluginOriginExternal: 'Harici', + pluginStatusAll: 'Tüm durumlar', + pluginEnabled: 'Etkin', + pluginDisabled: 'Devre dışı', + pluginCategory: 'Kategori', + pluginVerification: 'Doğrulama', + pluginOfficial: 'Resmî', + pluginCommunity: 'Topluluk', + pluginDownloads: 'İndirmeler', + pluginEnable: 'Etkinleştir', + pluginDisable: 'Devre dışı bırak', + pluginListEmpty: 'Eklenti yok', + pluginSearchEmpty: 'Eşleşen eklenti bulunamadı', + pluginInstallConfirm: '{0} eklentisi yüklensin mi?', + pluginDisableConfirm: '{0} eklentisi devre dışı bırakılsın mı?', + pluginUninstallConfirm: '{0} eklentisi kaldırılsın mı?', securityTab: 'Güvenlik', otherTab: 'Diğer', timeZone: 'Saat Dilimi', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index e64aa63e7..65c6a8999 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -714,6 +714,30 @@ const message = { verified: '驗證狀態', verifySkipped: '不驗證', skillsTab: '技能', + pluginsTab: '外掛程式', + pluginsInstalled: '已安裝', + pluginsMarket: '外掛程式市集', + pluginsMarketHint: '請輸入關鍵字搜尋外掛程式', + pluginSearchPlaceholder: '搜尋外掛程式...', + pluginOrigin: '來源', + pluginOriginAll: '全部來源', + pluginOriginBundled: '內建', + pluginOriginExternal: '外部', + pluginStatusAll: '全部狀態', + pluginEnabled: '已啟用', + pluginDisabled: '已停用', + pluginCategory: '分類', + pluginVerification: '驗證', + pluginOfficial: '官方', + pluginCommunity: '社群', + pluginDownloads: '下載量', + pluginEnable: '啟用', + pluginDisable: '停用', + pluginListEmpty: '暫無外掛程式', + pluginSearchEmpty: '找不到相關外掛程式', + pluginInstallConfirm: '確認安裝外掛程式 {0}?', + pluginDisableConfirm: '確認停用外掛程式 {0}?', + pluginUninstallConfirm: '確認解除安裝外掛程式 {0}?', securityTab: '安全', otherTab: '其他', timeZone: '時區', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 60c1f91ef..54e75fbfc 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -717,6 +717,30 @@ const message = { verified: '验证状态', verifySkipped: '不验证', skillsTab: '技能', + pluginsTab: '插件', + pluginsInstalled: '已安装', + pluginsMarket: '插件市场', + pluginsMarketHint: '请输入关键词搜索插件', + pluginSearchPlaceholder: '搜索插件...', + pluginOrigin: '来源', + pluginOriginAll: '全部来源', + pluginOriginBundled: '内置', + pluginOriginExternal: '外部', + pluginStatusAll: '全部状态', + pluginEnabled: '已启用', + pluginDisabled: '已禁用', + pluginCategory: '分类', + pluginVerification: '验证', + pluginOfficial: '官方', + pluginCommunity: '社区', + pluginDownloads: '下载量', + pluginEnable: '启用', + pluginDisable: '禁用', + pluginListEmpty: '暂无插件', + pluginSearchEmpty: '未找到相关插件', + pluginInstallConfirm: '确认安装插件 {0}?', + pluginDisableConfirm: '确认禁用插件 {0}?', + pluginUninstallConfirm: '确认卸载插件 {0}?', securityTab: '安全', otherTab: '其他', timeZone: '时区', diff --git a/frontend/src/views/ai/agents/agent/config/index.vue b/frontend/src/views/ai/agents/agent/config/index.vue index d9b24d1e6..3f73e5e7d 100644 --- a/frontend/src/views/ai/agents/agent/config/index.vue +++ b/frontend/src/views/ai/agents/agent/config/index.vue @@ -22,6 +22,9 @@ > + + + { @@ -115,6 +120,14 @@ const loadSkills = async () => { await skillsRef.value?.load(agentId.value); }; +const loadPlugins = async () => { + if (agentId.value <= 0) { + return; + } + await nextTick(); + await pluginsRef.value?.load(agentId.value); +}; + const handleClose = () => { activeTab.value = 'channels'; }; @@ -129,6 +142,9 @@ const handleTabClick = async (pane: TabsPaneContext) => { if (pane.paneName === 'skills') { await loadSkills(); } + if (pane.paneName === 'plugins') { + await loadPlugins(); + } if (pane.paneName === 'agent') { await loadAgent(); } diff --git a/frontend/src/views/ai/agents/agent/config/tabs/plugins.vue b/frontend/src/views/ai/agents/agent/config/tabs/plugins.vue new file mode 100644 index 000000000..5187d3580 --- /dev/null +++ b/frontend/src/views/ai/agents/agent/config/tabs/plugins.vue @@ -0,0 +1,357 @@ + + + + +