mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: add Feishu configuration for OpenClaw (#11832)
This commit is contained in:
@@ -189,3 +189,64 @@ func (b *BaseApi) DeleteAgentAccount(c *gin.Context) {
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Feishu channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentFeishuConfigReq true "request"
|
||||
// @Success 200 {object} dto.AgentFeishuConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/feishu/get [post]
|
||||
func (b *BaseApi) GetAgentFeishuConfig(c *gin.Context) {
|
||||
var req dto.AgentFeishuConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetFeishuConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Feishu channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentFeishuConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/feishu/update [post]
|
||||
func (b *BaseApi) UpdateAgentFeishuConfig(c *gin.Context) {
|
||||
var req dto.AgentFeishuConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateFeishuConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Approve Agent Feishu pairing code
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentFeishuPairingApproveReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/feishu/approve [post]
|
||||
func (b *BaseApi) ApproveAgentFeishuPairing(c *gin.Context) {
|
||||
var req dto.AgentFeishuPairingApproveReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.ApproveFeishuPairing(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type AgentItem struct {
|
||||
BridgePort int `json:"bridgePort"`
|
||||
Path string `json:"path"`
|
||||
ConfigPath string `json:"configPath"`
|
||||
Upgradable bool `json:"upgradable"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
@@ -108,3 +109,29 @@ type ProviderInfo struct {
|
||||
BaseURL string `json:"baseUrl"`
|
||||
Models []ProviderModelInfo `json:"models"`
|
||||
}
|
||||
|
||||
type AgentFeishuConfigReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentFeishuConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
BotName string `json:"botName" validate:"required"`
|
||||
AppID string `json:"appId" validate:"required"`
|
||||
AppSecret string `json:"appSecret" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentFeishuPairingApproveReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
PairingCode string `json:"pairingCode" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentFeishuConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy"`
|
||||
BotName string `json:"botName"`
|
||||
AppID string `json:"appId"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
}
|
||||
|
||||
+191
-1
@@ -20,6 +20,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
)
|
||||
|
||||
@@ -35,6 +37,9 @@ type IAgentService interface {
|
||||
PageAccounts(req dto.AgentAccountSearch) (int64, []dto.AgentAccountInfo, error)
|
||||
VerifyAccount(req dto.AgentAccountVerifyReq) error
|
||||
DeleteAccount(req dto.AgentAccountDeleteReq) error
|
||||
GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error)
|
||||
UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error
|
||||
ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error
|
||||
}
|
||||
|
||||
func NewIAgentService() IAgentService {
|
||||
@@ -172,7 +177,9 @@ func (a AgentService) Page(req dto.SearchWithPage) (int64, []dto.AgentItem, erro
|
||||
for _, item := range list {
|
||||
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(item.AppInstallID))
|
||||
envMap := readInstallEnv(appInstall.Env)
|
||||
items = append(items, buildAgentItem(&item, &appInstall, envMap))
|
||||
agentItem := buildAgentItem(&item, &appInstall, envMap)
|
||||
agentItem.Upgradable = checkAgentUpgradable(appInstall)
|
||||
items = append(items, agentItem)
|
||||
}
|
||||
return count, items, nil
|
||||
}
|
||||
@@ -349,6 +356,156 @@ func (a AgentService) DeleteAccount(req dto.AgentAccountDeleteReq) error {
|
||||
return agentAccountRepo.DeleteByID(req.ID)
|
||||
}
|
||||
|
||||
func (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) {
|
||||
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = install
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractFeishuConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error {
|
||||
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.DmPolicy == "" {
|
||||
req.DmPolicy = "pairing"
|
||||
}
|
||||
setFeishuConfig(conf, dto.AgentFeishuConfig{
|
||||
Enabled: req.Enabled,
|
||||
DmPolicy: req.DmPolicy,
|
||||
BotName: req.BotName,
|
||||
AppID: req.AppID,
|
||||
AppSecret: req.AppSecret,
|
||||
})
|
||||
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.RunDefaultBashCf(
|
||||
"docker exec %s openclaw pairing approve feishu %q",
|
||||
install.ContainerName,
|
||||
strings.TrimSpace(req.PairingCode),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) {
|
||||
agent, err := agentRepo.GetFirst(repo.WithByID(agentID))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if agent.AppInstallID == 0 {
|
||||
return nil, nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(agent.AppInstallID))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return agent, &install, nil
|
||||
}
|
||||
|
||||
func readOpenclawConfig(configPath string) (map[string]interface{}, error) {
|
||||
if strings.TrimSpace(configPath) == "" {
|
||||
return nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
content, err := fileOp.GetContent(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf := map[string]interface{}{}
|
||||
if err := json.Unmarshal(content, &conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func writeOpenclawConfigRaw(configPath string, conf map[string]interface{}) error {
|
||||
payload, err := json.MarshalIndent(conf, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
return fileOp.SaveFile(configPath, string(payload), 0600)
|
||||
}
|
||||
|
||||
func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
||||
result := dto.AgentFeishuConfig{Enabled: true, DmPolicy: "pairing"}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
feishu, ok := channels["feishu"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := feishu["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if dmPolicy, ok := feishu["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
|
||||
result.DmPolicy = dmPolicy
|
||||
}
|
||||
accounts, ok := feishu["accounts"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
main, ok := accounts["main"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if appID, ok := main["appId"].(string); ok {
|
||||
result.AppID = appID
|
||||
}
|
||||
if appSecret, ok := main["appSecret"].(string); ok {
|
||||
result.AppSecret = appSecret
|
||||
}
|
||||
if botName, ok := main["botName"].(string); ok {
|
||||
result.BotName = botName
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setFeishuConfig(conf map[string]interface{}, config dto.AgentFeishuConfig) {
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
channels = map[string]interface{}{}
|
||||
conf["channels"] = channels
|
||||
}
|
||||
feishu := map[string]interface{}{
|
||||
"enabled": config.Enabled,
|
||||
"dmPolicy": config.DmPolicy,
|
||||
"accounts": map[string]interface{}{
|
||||
"main": map[string]interface{}{
|
||||
"appId": config.AppID,
|
||||
"appSecret": config.AppSecret,
|
||||
"botName": config.BotName,
|
||||
},
|
||||
},
|
||||
}
|
||||
channels["feishu"] = feishu
|
||||
}
|
||||
|
||||
func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
|
||||
agents, err := agentRepo.List(repo.WithByAccountID(account.ID))
|
||||
if err != nil {
|
||||
@@ -436,6 +593,39 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
|
||||
return item
|
||||
}
|
||||
|
||||
func checkAgentUpgradable(install model.AppInstall) bool {
|
||||
if install.ID == 0 || install.Version == "" || install.Version == "latest" {
|
||||
return false
|
||||
}
|
||||
if install.App.ID == 0 {
|
||||
return false
|
||||
}
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(install.App.ID))
|
||||
if err != nil || len(details) == 0 {
|
||||
return false
|
||||
}
|
||||
versions := make([]string, 0, len(details))
|
||||
for _, item := range details {
|
||||
ignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(item.ID), appIgnoreUpgradeRepo.WithScope("version"))
|
||||
if len(ignores) > 0 {
|
||||
continue
|
||||
}
|
||||
if common.IsCrossVersion(install.Version, item.Version) && !install.App.CrossVersionUpdate {
|
||||
continue
|
||||
}
|
||||
versions = append(versions, item.Version)
|
||||
}
|
||||
if len(versions) == 0 {
|
||||
return false
|
||||
}
|
||||
versions = common.GetSortedVersions(versions)
|
||||
lastVersion := versions[0]
|
||||
if common.IsCrossVersion(install.Version, lastVersion) {
|
||||
return install.App.CrossVersionUpdate
|
||||
}
|
||||
return common.CompareVersion(lastVersion, install.Version)
|
||||
}
|
||||
|
||||
func (a AgentService) waitAndDeleteAgent(agentID uint, appInstallID uint) {
|
||||
if appInstallID == 0 {
|
||||
_ = agentRepo.DeleteByID(agentID)
|
||||
|
||||
@@ -49,5 +49,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.POST("/agents/accounts/search", baseApi.PageAgentAccounts)
|
||||
aiToolsRouter.POST("/agents/accounts/verify", baseApi.VerifyAgentAccount)
|
||||
aiToolsRouter.POST("/agents/accounts/delete", baseApi.DeleteAgentAccount)
|
||||
aiToolsRouter.POST("/agents/channel/feishu/get", baseApi.GetAgentFeishuConfig)
|
||||
aiToolsRouter.POST("/agents/channel/feishu/update", baseApi.UpdateAgentFeishuConfig)
|
||||
aiToolsRouter.POST("/agents/channel/feishu/approve", baseApi.ApproveAgentFeishuPairing)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +278,7 @@ export namespace AI {
|
||||
bridgePort: number;
|
||||
path: string;
|
||||
configPath: string;
|
||||
upgradable: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
@@ -343,4 +344,30 @@ export namespace AI {
|
||||
export interface AgentAccountDeleteReq {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface AgentFeishuConfigReq {
|
||||
agentId: number;
|
||||
}
|
||||
|
||||
export interface AgentFeishuConfig {
|
||||
enabled: boolean;
|
||||
dmPolicy: string;
|
||||
botName: string;
|
||||
appId: string;
|
||||
appSecret: string;
|
||||
}
|
||||
|
||||
export interface AgentFeishuConfigUpdateReq {
|
||||
agentId: number;
|
||||
enabled: boolean;
|
||||
dmPolicy: string;
|
||||
botName: string;
|
||||
appId: string;
|
||||
appSecret: string;
|
||||
}
|
||||
|
||||
export interface AgentFeishuPairingApproveReq {
|
||||
agentId: number;
|
||||
pairingCode: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,3 +127,15 @@ export const verifyAgentAccount = (req: AI.AgentAccountVerifyReq) => {
|
||||
export const deleteAgentAccount = (req: AI.AgentAccountDeleteReq) => {
|
||||
return http.post(`/ai/agents/accounts/delete`, req);
|
||||
};
|
||||
|
||||
export const getAgentFeishuConfig = (req: AI.AgentFeishuConfigReq) => {
|
||||
return http.post<AI.AgentFeishuConfig>(`/ai/agents/channel/feishu/get`, req);
|
||||
};
|
||||
|
||||
export const updateAgentFeishuConfig = (req: AI.AgentFeishuConfigUpdateReq) => {
|
||||
return http.post(`/ai/agents/channel/feishu/update`, req);
|
||||
};
|
||||
|
||||
export const approveAgentFeishuPairing = (req: AI.AgentFeishuPairingApproveReq) => {
|
||||
return http.post(`/ai/agents/channel/feishu/approve`, req);
|
||||
};
|
||||
|
||||
@@ -695,6 +695,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: 'Manual input',
|
||||
verified: 'Verified',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Models',
|
||||
|
||||
@@ -691,6 +691,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: 'Entrada manual de modelo',
|
||||
verified: 'Verificado',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Modelo',
|
||||
|
||||
@@ -680,6 +680,21 @@ const message = {
|
||||
token: 'トークン',
|
||||
manualModel: '手動入力',
|
||||
verified: '検証済み',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'モデル',
|
||||
|
||||
@@ -677,6 +677,21 @@ const message = {
|
||||
token: '토큰',
|
||||
manualModel: '수동 입력',
|
||||
verified: '검증됨',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: '모델',
|
||||
|
||||
@@ -692,6 +692,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: 'Input manual',
|
||||
verified: 'Disahkan',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Model',
|
||||
|
||||
@@ -689,6 +689,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: 'Entrada manual',
|
||||
verified: 'Verificado',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Modelo',
|
||||
|
||||
@@ -685,6 +685,21 @@ const message = {
|
||||
token: 'Токен',
|
||||
manualModel: 'Ручной ввод',
|
||||
verified: 'Проверено',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Модель',
|
||||
|
||||
@@ -699,6 +699,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: 'Manuel giriş',
|
||||
verified: 'Doğrulandı',
|
||||
configTitle: 'Configuration',
|
||||
channelsTab: 'Channels',
|
||||
feishu: 'Feishu',
|
||||
dmPolicy: 'DM Policy',
|
||||
botName: 'Bot Name',
|
||||
appId: 'App ID',
|
||||
appSecret: 'App Secret',
|
||||
saveAndRestartGateway: 'Save and restart gateway',
|
||||
pairingCode: 'Pairing Code',
|
||||
pairingCodePlaceholder: 'Enter pairing code',
|
||||
approvePairing: 'Approve Pairing',
|
||||
feishuRequired: 'Please fill botName / appId / appSecret',
|
||||
feishuSaveSuccess: 'Saved successfully',
|
||||
pairingCodeRequired: 'Please enter pairing code',
|
||||
pairingApproveSuccess: 'Pairing approved successfully',
|
||||
},
|
||||
model: {
|
||||
model: 'Model',
|
||||
|
||||
@@ -667,6 +667,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: '手動輸入模型',
|
||||
verified: '驗證狀態',
|
||||
configTitle: '配置',
|
||||
channelsTab: '聊天渠道',
|
||||
feishu: '飛書',
|
||||
dmPolicy: '私聊策略',
|
||||
botName: '機器人名稱',
|
||||
appId: '應用 App ID',
|
||||
appSecret: '應用 App Secret',
|
||||
saveAndRestartGateway: '保存並重啟網關',
|
||||
pairingCode: '配對碼',
|
||||
pairingCodePlaceholder: '請輸入配對碼',
|
||||
approvePairing: '批准配對',
|
||||
feishuRequired: '請填寫 botName / appId / appSecret',
|
||||
feishuSaveSuccess: '保存成功',
|
||||
pairingCodeRequired: '請輸入配對碼',
|
||||
pairingApproveSuccess: '配對成功',
|
||||
},
|
||||
model: {
|
||||
model: '模型',
|
||||
|
||||
@@ -669,6 +669,21 @@ const message = {
|
||||
token: 'Token',
|
||||
manualModel: '手动输入模型',
|
||||
verified: '验证状态',
|
||||
configTitle: '配置',
|
||||
channelsTab: '聊天渠道',
|
||||
feishu: '飞书',
|
||||
dmPolicy: '私聊策略',
|
||||
botName: '机器人名称',
|
||||
appId: '应用 App ID',
|
||||
appSecret: '应用 App Secret',
|
||||
saveAndRestartGateway: '保存并重启网关',
|
||||
pairingCode: '配对码',
|
||||
pairingCodePlaceholder: '请输入配对码',
|
||||
approvePairing: '批准配对',
|
||||
feishuRequired: '请填写 botName / appId / appSecret',
|
||||
feishuSaveSuccess: '保存成功',
|
||||
pairingCodeRequired: '请输入配对码',
|
||||
pairingApproveSuccess: '配对成功',
|
||||
},
|
||||
model: {
|
||||
model: '模型',
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<DrawerPro v-model="open" :header="header" size="large" @close="handleClose">
|
||||
<el-tabs v-model="activeTab" tab-position="left" class="config-tabs" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="t('aiTools.agents.channelsTab')" name="channels">
|
||||
<ChannelsTab ref="channelsRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</DrawerPro>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref } from 'vue';
|
||||
import type { TabsPaneContext } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import ChannelsTab from './tabs/channels.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const open = ref(false);
|
||||
const activeTab = ref('channels');
|
||||
const header = ref('');
|
||||
const agentId = ref(0);
|
||||
const channelsRef = ref();
|
||||
|
||||
const loadChannels = async () => {
|
||||
if (agentId.value <= 0) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await channelsRef.value?.load(agentId.value);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
activeTab.value = 'channels';
|
||||
};
|
||||
|
||||
const handleTabClick = async (pane: TabsPaneContext) => {
|
||||
if (pane.paneName === 'channels' && agentId.value > 0) {
|
||||
await loadChannels();
|
||||
}
|
||||
};
|
||||
|
||||
const openDrawer = async (agent: AI.AgentItem) => {
|
||||
agentId.value = agent.id;
|
||||
header.value = `${agent.name} - ${t('aiTools.agents.configTitle')}`;
|
||||
activeTab.value = 'channels';
|
||||
open.value = true;
|
||||
await loadChannels();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open: openDrawer,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.config-tabs {
|
||||
min-height: 440px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
|
||||
<el-form-item :label="t('aiTools.agents.feishu')">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
|
||||
<el-select v-model="form.dmPolicy">
|
||||
<el-option label="pairing" value="pairing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.botName')" prop="botName">
|
||||
<el-input v-model="form.botName" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.appId')" prop="appId">
|
||||
<el-input v-model="form.appId" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.appSecret')" prop="appSecret">
|
||||
<el-input v-model="form.appSecret" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="saveChannel">
|
||||
{{ t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<el-form-item :label="t('aiTools.agents.pairingCode')">
|
||||
<el-input v-model="pairingCode" :placeholder="t('aiTools.agents.pairingCodePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="approving" @click="approvePairing">
|
||||
{{ t('aiTools.agents.approvePairing') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { approveAgentFeishuPairing, getAgentFeishuConfig, updateAgentFeishuConfig } from '@/api/modules/ai';
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
|
||||
const { t } = useI18n();
|
||||
const saving = ref(false);
|
||||
const approving = ref(false);
|
||||
const agentId = ref(0);
|
||||
const pairingCode = ref('');
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive<AI.AgentFeishuConfig>({
|
||||
enabled: true,
|
||||
dmPolicy: 'pairing',
|
||||
botName: '',
|
||||
appId: '',
|
||||
appSecret: '',
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
dmPolicy: [Rules.requiredSelect],
|
||||
botName: [Rules.requiredInput],
|
||||
appId: [Rules.requiredInput],
|
||||
appSecret: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const load = async (id: number) => {
|
||||
agentId.value = id;
|
||||
pairingCode.value = '';
|
||||
const res = await getAgentFeishuConfig({ agentId: id });
|
||||
Object.assign(form, res.data || {});
|
||||
if (!form.dmPolicy) {
|
||||
form.dmPolicy = 'pairing';
|
||||
}
|
||||
};
|
||||
|
||||
const saveChannel = async () => {
|
||||
if (!agentId.value) {
|
||||
return;
|
||||
}
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
await formRef.value.validate();
|
||||
saving.value = true;
|
||||
try {
|
||||
await updateAgentFeishuConfig({
|
||||
agentId: agentId.value,
|
||||
enabled: form.enabled,
|
||||
dmPolicy: form.dmPolicy || 'pairing',
|
||||
botName: form.botName,
|
||||
appId: form.appId,
|
||||
appSecret: form.appSecret,
|
||||
});
|
||||
MsgSuccess(t('aiTools.agents.feishuSaveSuccess'));
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const approvePairing = async () => {
|
||||
if (!agentId.value) {
|
||||
return;
|
||||
}
|
||||
if (!pairingCode.value) {
|
||||
MsgWarning(t('aiTools.agents.pairingCodeRequired'));
|
||||
return;
|
||||
}
|
||||
approving.value = true;
|
||||
try {
|
||||
await approveAgentFeishuPairing({
|
||||
agentId: agentId.value,
|
||||
pairingCode: pairingCode.value,
|
||||
});
|
||||
MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));
|
||||
pairingCode.value = '';
|
||||
} finally {
|
||||
approving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
load,
|
||||
});
|
||||
</script>
|
||||
@@ -23,7 +23,14 @@
|
||||
<Status :status="row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('aiTools.agents.appVersion')" prop="appVersion" min-width="100" />
|
||||
<el-table-column :label="$t('aiTools.agents.appVersion')" prop="appVersion" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.appVersion }}</span>
|
||||
<el-button v-if="row.upgradable" link type="primary" class="ml-1" @click="openUpgrade(row)">
|
||||
{{ $t('commons.button.upgrade') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('aiTools.model.model')"
|
||||
show-overflow-tooltip
|
||||
@@ -66,7 +73,7 @@
|
||||
min-width="220"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
:ellipsis="2"
|
||||
:ellipsis="3"
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
@@ -74,6 +81,8 @@
|
||||
<AddDialog ref="addRef" @search="search" @task="openTaskLog" />
|
||||
<TaskLog ref="taskLogRef" @close="search" />
|
||||
<DeleteDialog ref="deleteRef" @close="search" />
|
||||
<ConfigDrawer ref="configRef" />
|
||||
<AppUpgrade ref="upgradeRef" @close="search" />
|
||||
<ComposeLogs ref="composeLogRef" />
|
||||
<TerminalDialog ref="dialogTerminalRef" />
|
||||
<PortJumpDialog ref="dialogPortJumpRef" />
|
||||
@@ -83,14 +92,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { pageAgents } from '@/api/modules/ai';
|
||||
import { installedOp } from '@/api/modules/app';
|
||||
import { installedOp, searchAppInstalled } from '@/api/modules/app';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { App } from '@/api/interface/app';
|
||||
import { SearchWithPage } from '@/api/interface';
|
||||
import { dateFormat, newUUID } from '@/utils/util';
|
||||
|
||||
import RouterMenu from '@/views/ai/agents/index.vue';
|
||||
import AddDialog from '@/views/ai/agents/agent/add/index.vue';
|
||||
import DeleteDialog from '@/views/ai/agents/agent/delete/index.vue';
|
||||
import ConfigDrawer from '@/views/ai/agents/agent/config/index.vue';
|
||||
import AppUpgrade from '@/views/app-store/installed/upgrade/index.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import ComposeLogs from '@/components/log/compose/index.vue';
|
||||
import TerminalDialog from '@/views/container/container/terminal/index.vue';
|
||||
@@ -103,6 +115,8 @@ const loading = ref(false);
|
||||
const addRef = ref();
|
||||
const taskLogRef = ref();
|
||||
const deleteRef = ref();
|
||||
const configRef = ref();
|
||||
const upgradeRef = ref();
|
||||
const composeLogRef = ref();
|
||||
const dialogTerminalRef = ref();
|
||||
const dialogPortJumpRef = ref();
|
||||
@@ -124,6 +138,10 @@ const getProviderLabel = (value: string) => {
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.set'),
|
||||
click: (row: AI.AgentItem) => openConfig(row),
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('menu.terminal'),
|
||||
click: (row: AI.AgentItem) => openTerminal(row),
|
||||
@@ -146,6 +164,11 @@ const buttons = [
|
||||
label: i18n.global.t('commons.operate.restart'),
|
||||
click: (row: AI.AgentItem) => onOperate(row, 'restart'),
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.upgrade'),
|
||||
click: (row: AI.AgentItem) => openUpgrade(row),
|
||||
disabled: (row: AI.AgentItem) => !row.upgradable,
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: AI.AgentItem) => onDelete(row),
|
||||
@@ -232,6 +255,19 @@ const onDelete = (row: AI.AgentItem) => {
|
||||
deleteRef.value?.acceptParams(row.id, row.name);
|
||||
};
|
||||
|
||||
const openConfig = (row: AI.AgentItem) => {
|
||||
configRef.value?.open(row);
|
||||
};
|
||||
|
||||
const openUpgrade = async (row: AI.AgentItem) => {
|
||||
const res = await searchAppInstalled({ page: 1, pageSize: 200, name: row.name });
|
||||
const appInstall = (res.data.items || []).find((item: App.AppInstallDto) => item.id === row.appInstallId);
|
||||
if (!appInstall) {
|
||||
return;
|
||||
}
|
||||
upgradeRef.value?.acceptParams(appInstall, 'upgrade');
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await search();
|
||||
});
|
||||
|
||||
@@ -293,7 +293,6 @@ const acceptParams = async (props: ParamProps) => {
|
||||
await get();
|
||||
open.value = true;
|
||||
openConfig.value = false;
|
||||
console.log('params', params.value);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
|
||||
@@ -545,7 +545,6 @@ const passkeyLogin = async () => {
|
||||
} catch (res: any) {
|
||||
if (res?.message) {
|
||||
MsgError(i18n.t('commons.login.passkeyFailed'));
|
||||
console.log(res.message);
|
||||
}
|
||||
} finally {
|
||||
isLoggingIn = false;
|
||||
|
||||
Reference in New Issue
Block a user