feat: QwenPaw support config username/password (#13478)

This commit is contained in:
CityFun authored and GitHub committed 2026-08-05 16:35:09 +08:00
1 parent 9dfa451fae
commit 83a3675a0c
35 files changed
+346 -89

No files matched your search

+2 -2
View File
@@ -730,9 +730,9 @@ type AgentSecurityConfig struct {
type AgentOtherConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
UserTimezone string `json:"userTimezone" validate:"required"`
UserTimezone string `json:"userTimezone"`
BrowserEnabled bool `json:"browserEnabled"`
NPMRegistry string `json:"npmRegistry" validate:"required"`
NPMRegistry string `json:"npmRegistry"`
DashboardUsername string `json:"dashboardUsername"`
DashboardPassword string `json:"dashboardPassword"`
}
+31 -10
View File
@@ -181,7 +181,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
var allowedOrigins []string
var account *model.AgentAccount
var installHooks *appInstallHooks
var hermesAuth hermesDashboardAuth
var dashboardAuth agentDashboardAuth
if agentType == constant.AppOpenclaw || agentType == constant.AppHermesAgent {
if req.AccountID == 0 {
@@ -226,15 +226,17 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
},
}
} else if agentType == constant.AppHermesAgent {
hermesAuth = normalizeHermesDashboardAuth(req.DashboardUsername, req.DashboardPassword)
dashboardAuth = normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword)
installHooks = &appInstallHooks{
AfterCopyData: func(appInstall *model.AppInstall) error {
if err := prepareHermesInstallFiles(appInstall, account, storedModel); err != nil {
return err
}
return writeHermesDashboardAuthEnv(path.Join(appInstall.GetPath(), ".env"), hermesAuth, false)
return writeAgentDashboardAuthEnv(appInstall.GetEnvPath(), agentType, dashboardAuth, false)
},
}
} else if agentType == constant.AppCopaw {
dashboardAuth = normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword)
}
params := map[string]interface{}{
@@ -254,9 +256,12 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
params["API_KEY"] = apiKey
params["OPENCLAW_GATEWAY_TOKEN"] = token
}
if agentType == constant.AppHermesAgent {
params[hermesDashboardUsernameEnvKey] = hermesAuth.Username
params[hermesDashboardPasswordEnvKey] = hermesAuth.Password
if usernameKey, passwordKey, ok := agentDashboardAuthEnvKeys(agentType); ok {
params[usernameKey] = dashboardAuth.Username
params[passwordKey] = dashboardAuth.Password
if agentType == constant.AppCopaw {
params[qwenPawAuthEnabledEnvKey] = "true"
}
}
if req.EditCompose && strings.TrimSpace(req.DockerCompose) == "" {
@@ -1435,7 +1440,7 @@ func (a AgentService) GetOtherConfig(req dto.AgentIDReq) (*dto.AgentOtherConfig,
if err != nil {
return nil, err
}
auth := readHermesDashboardAuthFromInstall(install)
auth := readAgentDashboardAuthFromInstall(install, agent.AgentType)
return &dto.AgentOtherConfig{
UserTimezone: cfg.Timezone,
BrowserEnabled: true,
@@ -1444,6 +1449,13 @@ func (a AgentService) GetOtherConfig(req dto.AgentIDReq) (*dto.AgentOtherConfig,
DashboardPassword: auth.Password,
}, nil
}
if agent.AgentType == constant.AppCopaw {
auth := readAgentDashboardAuthFromInstall(install, agent.AgentType)
return &dto.AgentOtherConfig{
DashboardUsername: auth.Username,
DashboardPassword: auth.Password,
}, nil
}
conf, err := readOpenclawConfig(agent.ConfigPath)
if err != nil {
return nil, err
@@ -1462,16 +1474,19 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
return err
}
if agent.AgentType == constant.AppHermesAgent {
if strings.TrimSpace(req.UserTimezone) == "" {
return buserr.New("ErrInvalidParams")
}
account, err := agentAccountRepo.GetFirst(repo.WithByID(agent.AccountID))
if err != nil {
return err
}
previousAuth := readHermesDashboardAuthFromInstall(install)
nextAuth := normalizeHermesDashboardAuth(req.DashboardUsername, req.DashboardPassword)
previousAuth := readAgentDashboardAuthFromInstall(install, agent.AgentType)
nextAuth := normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword)
if err := writeHermesConfig(path.Dir(agent.ConfigPath), account, agent.Model, strings.TrimSpace(req.UserTimezone)); err != nil {
return err
}
if err := writeHermesDashboardAuthEnv(path.Join(install.GetPath(), ".env"), nextAuth, true); err != nil {
if err := writeAgentDashboardAuthEnv(install.GetEnvPath(), agent.AgentType, nextAuth, true); err != nil {
return err
}
operate := constant.Restart
@@ -1483,6 +1498,12 @@ func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
Operate: operate,
})
}
if agent.AgentType == constant.AppCopaw {
return updateQwenPawDashboardAuth(install, normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword))
}
if strings.TrimSpace(req.UserTimezone) == "" || strings.TrimSpace(req.NPMRegistry) == "" {
return buserr.New("ErrInvalidParams")
}
if err := ensureContainerRunning(install.ContainerName); err != nil {
return err
}
+128
View File
@@ -0,0 +1,128 @@
package service
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
)
type qwenPawAuthStatus struct {
Enabled bool `json:"enabled"`
HasUsers bool `json:"has_users"`
}
type qwenPawLoginResponse struct {
Token string `json:"token"`
}
func updateQwenPawDashboardAuth(install *model.AppInstall, next agentDashboardAuth) error {
if install == nil || install.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
current, err := readAgentDashboardAuthEnv(install.GetEnvPath(), constant.AppCopaw)
if err != nil {
return err
}
if current == next {
return writeAgentDashboardAuthEnv(install.GetEnvPath(), constant.AppCopaw, next, true)
}
if err := ensureContainerRunning(install.ContainerName); err != nil {
return err
}
baseURL := fmt.Sprintf("http://127.0.0.1:%d/api/auth", install.HttpPort)
var status qwenPawAuthStatus
if _, err := requestQwenPawAuth(http.MethodGet, baseURL+"/status", nil, "", &status); err != nil {
return buserr.WithMap("ErrQwenPawAuthRequest", map[string]interface{}{"err": err.Error()}, err)
}
if !status.Enabled {
return buserr.New("ErrQwenPawAuthDisabled")
}
if !status.HasUsers {
payload := map[string]string{"username": next.Username, "password": next.Password}
if _, err := requestQwenPawAuth(http.MethodPost, baseURL+"/register", payload, "", nil); err != nil {
return buserr.WithMap("ErrQwenPawAuthRequest", map[string]interface{}{"err": err.Error()}, err)
}
} else {
var login qwenPawLoginResponse
payload := map[string]string{"username": current.Username, "password": current.Password}
statusCode, err := requestQwenPawAuth(http.MethodPost, baseURL+"/login", payload, "", &login)
if statusCode == http.StatusUnauthorized {
return buserr.New("ErrQwenPawAuthOutOfSync")
}
if err != nil {
return buserr.WithMap("ErrQwenPawAuthRequest", map[string]interface{}{"err": err.Error()}, err)
}
payload = map[string]string{"current_password": current.Password}
if current.Username != next.Username {
payload["new_username"] = next.Username
}
if current.Password != next.Password {
payload["new_password"] = next.Password
}
if _, err := requestQwenPawAuth(http.MethodPost, baseURL+"/update-profile", payload, login.Token, nil); err != nil {
return buserr.WithMap("ErrQwenPawAuthRequest", map[string]interface{}{"err": err.Error()}, err)
}
}
return writeAgentDashboardAuthEnv(install.GetEnvPath(), constant.AppCopaw, next, true)
}
func requestQwenPawAuth(method, reqURL string, payload interface{}, token string, result interface{}) (int, error) {
var body io.Reader
if payload != nil {
data, err := json.Marshal(payload)
if err != nil {
return 0, err
}
body = bytes.NewReader(data)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, method, reqURL, body)
if err != nil {
return 0, err
}
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
data, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return resp.StatusCode, err
}
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
detail := strings.TrimSpace(string(data))
var errorResponse struct {
Detail string `json:"detail"`
}
if json.Unmarshal(data, &errorResponse) == nil && strings.TrimSpace(errorResponse.Detail) != "" {
detail = strings.TrimSpace(errorResponse.Detail)
}
if detail == "" {
detail = resp.Status
}
return resp.StatusCode, errors.New(detail)
}
if result != nil && len(data) > 0 {
if err := json.Unmarshal(data, result); err != nil {
return resp.StatusCode, err
}
}
return resp.StatusCode, nil
}
-53
View File
@@ -19,13 +19,6 @@ import (
const hermesWorkspaceDir = "/opt/data/workspace"
const hermesExecutablePath = "/opt/hermes/.venv/bin/hermes"
const hermesDashboardUsernameEnvKey = "HERMES_DASHBOARD_USERNAME"
const hermesDashboardPasswordEnvKey = "HERMES_DASHBOARD_PASSWORD"
type hermesDashboardAuth struct {
Username string
Password string
}
type hermesConfig struct {
Model hermesModelConfig `yaml:"model"`
@@ -117,52 +110,6 @@ func prepareHermesInstallFiles(appInstall *model.AppInstall, account *model.Agen
return files.NewFileOp().ChownR(dataDir, "1000", "1000", true)
}
func normalizeHermesDashboardAuth(username, password string) hermesDashboardAuth {
auth := hermesDashboardAuth{
Username: strings.TrimSpace(username),
Password: strings.TrimSpace(password),
}
if auth.Username == "" {
auth.Username = "admin"
}
if auth.Password == "" {
auth.Password = common.RandStr(8)
}
return auth
}
func writeHermesDashboardAuthEnv(envPath string, auth hermesDashboardAuth, overwrite bool) error {
return upsertAgentEnv(envPath, map[string]string{
hermesDashboardUsernameEnvKey: auth.Username,
hermesDashboardPasswordEnvKey: auth.Password,
}, []string{
hermesDashboardUsernameEnvKey,
hermesDashboardPasswordEnvKey,
}, overwrite)
}
func readHermesDashboardAuthEnv(envPath string) (hermesDashboardAuth, error) {
envMap, err := readAgentEnvMap(envPath)
if err != nil {
return hermesDashboardAuth{}, err
}
return hermesDashboardAuth{
Username: strings.TrimSpace(envMap[hermesDashboardUsernameEnvKey]),
Password: strings.TrimSpace(envMap[hermesDashboardPasswordEnvKey]),
}, nil
}
func readHermesDashboardAuthFromInstall(appInstall *model.AppInstall) hermesDashboardAuth {
if appInstall == nil || appInstall.ID == 0 {
return hermesDashboardAuth{}
}
auth, err := readHermesDashboardAuthEnv(path.Join(appInstall.GetPath(), ".env"))
if err != nil {
return hermesDashboardAuth{}
}
return auth
}
func readHermesConfig(configPath string) (*hermesConfig, error) {
content, err := files.NewFileOp().GetContent(configPath)
if err != nil {
+83 -2
View File
@@ -422,8 +422,8 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
item.BridgePort = toInt(bridge)
}
}
if agentType == constant.AppHermesAgent {
auth := readHermesDashboardAuthFromInstall(appInstall)
if _, _, ok := agentDashboardAuthEnvKeys(agentType); ok {
auth := readAgentDashboardAuthFromInstall(appInstall, agentType)
item.DashboardUsername = auth.Username
item.DashboardPassword = auth.Password
}
@@ -1463,6 +1463,87 @@ func readInstallEnv(envStr string) map[string]interface{} {
return data
}
const (
hermesDashboardUsernameEnvKey = "HERMES_DASHBOARD_USERNAME"
hermesDashboardPasswordEnvKey = "HERMES_DASHBOARD_PASSWORD"
qwenPawAuthEnabledEnvKey = "QWENPAW_AUTH_ENABLED"
qwenPawAuthUsernameEnvKey = "QWENPAW_AUTH_USERNAME"
qwenPawAuthPasswordEnvKey = "QWENPAW_AUTH_PASSWORD"
)
type agentDashboardAuth struct {
Username string
Password string
}
func normalizeAgentDashboardAuth(username, password string) agentDashboardAuth {
auth := agentDashboardAuth{
Username: strings.TrimSpace(username),
Password: strings.TrimSpace(password),
}
if auth.Username == "" {
auth.Username = "admin"
}
if auth.Password == "" {
auth.Password = common.RandStr(8)
}
return auth
}
func agentDashboardAuthEnvKeys(agentType string) (string, string, bool) {
switch agentType {
case constant.AppHermesAgent:
return hermesDashboardUsernameEnvKey, hermesDashboardPasswordEnvKey, true
case constant.AppCopaw:
return qwenPawAuthUsernameEnvKey, qwenPawAuthPasswordEnvKey, true
default:
return "", "", false
}
}
func writeAgentDashboardAuthEnv(envPath, agentType string, auth agentDashboardAuth, overwrite bool) error {
usernameKey, passwordKey, ok := agentDashboardAuthEnvKeys(agentType)
if !ok {
return fmt.Errorf("dashboard auth is not supported for %s", agentType)
}
values := map[string]string{
usernameKey: auth.Username,
passwordKey: auth.Password,
}
order := []string{usernameKey, passwordKey}
if agentType == constant.AppCopaw {
values[qwenPawAuthEnabledEnvKey] = "true"
order = append([]string{qwenPawAuthEnabledEnvKey}, order...)
}
return upsertAgentEnv(envPath, values, order, overwrite)
}
func readAgentDashboardAuthEnv(envPath, agentType string) (agentDashboardAuth, error) {
usernameKey, passwordKey, ok := agentDashboardAuthEnvKeys(agentType)
if !ok {
return agentDashboardAuth{}, fmt.Errorf("dashboard auth is not supported for %s", agentType)
}
envMap, err := readAgentEnvMap(envPath)
if err != nil {
return agentDashboardAuth{}, err
}
return agentDashboardAuth{
Username: strings.TrimSpace(envMap[usernameKey]),
Password: strings.TrimSpace(envMap[passwordKey]),
}, nil
}
func readAgentDashboardAuthFromInstall(appInstall *model.AppInstall, agentType string) agentDashboardAuth {
if appInstall == nil || appInstall.ID == 0 {
return agentDashboardAuth{}
}
auth, err := readAgentDashboardAuthEnv(appInstall.GetEnvPath(), agentType)
if err != nil {
return agentDashboardAuth{}
}
return auth
}
func readAgentEnvMap(envPath string) (map[string]string, error) {
fileOp := files.NewFileOp()
if !fileOp.Stat(envPath) {
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'This agent is already bound to a website'
ErrAgentWebsiteTypeUnsupported: 'Only proxy or static websites can be bound'
ErrAgentWebsiteInUse: 'This website is already bound to another agent'
ErrAgentWebsiteUnbindUnsupported: 'Deployment websites cannot be unbound manually'
ErrQwenPawAuthRequest: 'Failed to update QwenPaw credentials: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'The current QwenPaw credentials do not match the 1Panel record. Reset the QwenPaw password or restore the credentials in the application .env file'
ErrQwenPawAuthDisabled: 'Login authentication is not enabled in QwenPaw'
ErrHermesPairingCodeUnavailable: 'The pairing code is temporarily unavailable in Hermes, possibly due to network issues. Please try again later.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'When the Feishu group policy is Allowlist, the DM policy cannot be Pairing Code.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'Este agente ya está vinculado a un sitio web'
ErrAgentWebsiteTypeUnsupported: 'Solo se pueden vincular sitios proxy o estáticos'
ErrAgentWebsiteInUse: 'Este sitio web ya está vinculado a otro agente'
ErrAgentWebsiteUnbindUnsupported: 'Los sitios web de despliegue no se pueden desvincular manualmente'
ErrQwenPawAuthRequest: 'No se pudieron actualizar las credenciales de QwenPaw: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'Las credenciales actuales de QwenPaw no coinciden con el registro de 1Panel. Restablezca la contraseña de QwenPaw o restaure las credenciales en el archivo .env de la aplicación'
ErrQwenPawAuthDisabled: 'La autenticación de inicio de sesión no está habilitada en QwenPaw'
ErrHermesPairingCodeUnavailable: 'El código de emparejamiento no está disponible temporalmente en Hermes, posiblemente por un problema de red. Inténtalo de nuevo más tarde.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Cuando la política de grupo de Feishu es Lista permitida, la política de MD no puede ser Código de emparejamiento.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'این عامل قبلاً به یک وب‌سایت مت
ErrAgentWebsiteTypeUnsupported: 'فقط وب‌سایت‌های پراکسی یا استاتیک قابل اتصال هستند'
ErrAgentWebsiteInUse: 'این وب‌سایت قبلاً به عامل دیگری متصل است'
ErrAgentWebsiteUnbindUnsupported: 'وب‌سایت‌های استقرار یافته را نمی‌توان به صورت دستی قطع اتصال کرد'
ErrQwenPawAuthRequest: 'به‌روزرسانی اطلاعات ورود QwenPaw ناموفق بود: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'اطلاعات ورود فعلی QwenPaw با رکورد 1Panel مطابقت ندارد. گذرواژه QwenPaw را بازنشانی کنید یا اطلاعات ورود فایل .env برنامه را بازیابی کنید'
ErrQwenPawAuthDisabled: 'احراز هویت ورود در QwenPaw فعال نشده است'
ErrHermesPairingCodeUnavailable: 'کد جفت‌سازی به طور موقت در هرمس در دسترس نیست، احتمالاً به دلیل مشکلات شبکه. لطفاً بعداً دوباره تلاش کنید.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'وقتی خط مشی گروه فیشو لیست سفید باشد، خط مشی DM نمی‌تواند کد جفت‌سازی باشد.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'このエージェントはすでにサイトに関連付
ErrAgentWebsiteTypeUnsupported: '関連付けできるのはプロキシサイトまたは静的サイトのみです'
ErrAgentWebsiteInUse: 'このサイトはすでに別のエージェントに関連付けられています'
ErrAgentWebsiteUnbindUnsupported: 'ワンクリックデプロイのサイトは手動で関連解除できません'
ErrQwenPawAuthRequest: 'QwenPaw 認証情報の更新に失敗しました: {{ .err }}'
ErrQwenPawAuthOutOfSync: '現在の QwenPaw 認証情報が 1Panel の記録と一致しません。QwenPaw のパスワードをリセットするか、アプリケーションの .env にある認証情報を復元してください'
ErrQwenPawAuthDisabled: 'QwenPaw でログイン認証が有効になっていません'
ErrHermesPairingCodeUnavailable: 'Hermes でペアリングコードが一時的に見つかりません。ネットワーク要因の可能性があるため、しばらくしてから再試行してください。'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Feishu のグループポリシーが許可リストの場合、DM ポリシーをペアリングコードにはできません。'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: '이 에이전트는 이미 웹사이트에 연결되어
ErrAgentWebsiteTypeUnsupported: '프록시 또는 정적 웹사이트만 연결할 수 있습니다'
ErrAgentWebsiteInUse: '이 웹사이트는 이미 다른 에이전트에 연결되어 있습니다'
ErrAgentWebsiteUnbindUnsupported: '원클릭 배포 웹사이트는 수동으로 연결 해제할 수 없습니다'
ErrQwenPawAuthRequest: 'QwenPaw 인증 정보 업데이트에 실패했습니다: {{ .err }}'
ErrQwenPawAuthOutOfSync: '현재 QwenPaw 자격 증명이 1Panel 기록과 일치하지 않습니다. QwenPaw 비밀번호를 재설정하거나 애플리케이션 .env의 자격 증명을 복원하세요'
ErrQwenPawAuthDisabled: 'QwenPaw에서 로그인 인증이 활성화되어 있지 않습니다'
ErrHermesPairingCodeUnavailable: 'Hermes에서 페어링 코드가 일시적으로 존재하지 않습니다. 네트워크 문제일 수 있으니 잠시 후 다시 시도해 주세요.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Feishu 그룹 정책이 허용 목록이면 DM 정책을 페어링 코드로 설정할 수 없습니다.'
+3
View File
@@ -78,6 +78,9 @@ ErrAgentWebsiteBound: 'ຕົວແທນນີ້ຖືກຜູກມັດກ
ErrAgentWebsiteTypeUnsupported: 'ສາມາດຜູກມັດໄດ້ສະເພາະເວັບໄຊປະເພດ Proxy ຫຼື Static ເທົ່ານັ້ນ'
ErrAgentWebsiteInUse: 'ເວັບໄຊນີ້ຖືກຜູກມັດກັບຕົວແທນອື່ນແລ້ວ'
ErrAgentWebsiteUnbindUnsupported: 'ເວັບໄຊທີ່ຕິດຕັ້ງແລ້ວບໍ່ສາມາດຍົກເລີກການຜູກມັດດ້ວຍຕົນເອງໄດ້'
ErrQwenPawAuthRequest: 'ອັບເດດຂໍ້ມູນຢືນຢັນ QwenPaw ບໍ່ສຳເລັດ: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'ຂໍ້ມູນຢືນຢັນ QwenPaw ປັດຈຸບັນບໍ່ກົງກັບບັນທຶກຂອງ 1Panel. ກະລຸນາຣີເຊັດລະຫັດຜ່ານ QwenPaw ຫຼືກູ້ຄືນຂໍ້ມູນໃນໄຟລ໌ .env ຂອງແອັບ'
ErrQwenPawAuthDisabled: 'QwenPaw ຍັງບໍ່ໄດ້ເປີດໃຊ້ການຢືນຢັນການເຂົ້າລະບົບ'
ErrHermesPairingCodeUnavailable: 'ລະຫັດການຈັບຄູ່ໃນ Hermes ບໍ່ສາມາດໃຊ້ງານໄດ້ຊົ່ວຄາວ, ອາດຈະເກີດຈາກບັນຫາເຄືອຂ່າຍ. ກະລຸນາລອງໃໝ່ພາຍຫຼັງ.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'ເມື່ອນະໂຍບາຍກຸ່ມ Feishu ແມ່ນ Allowlist, ນະໂຍບາຍ DM ບໍ່ສາມາດເປັນ Pairing Code ໄດ້.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'Ejen ini sudah dipautkan ke laman web'
ErrAgentWebsiteTypeUnsupported: 'Hanya laman web proxy atau statik boleh dipautkan'
ErrAgentWebsiteInUse: 'Laman web ini sudah dipautkan ke ejen lain'
ErrAgentWebsiteUnbindUnsupported: 'Laman web one-click deployment tidak menyokong nyahikat manual'
ErrQwenPawAuthRequest: 'Gagal mengemas kini kelayakan QwenPaw: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'Kelayakan QwenPaw semasa tidak sepadan dengan rekod 1Panel. Tetapkan semula kata laluan QwenPaw atau pulihkan kelayakan dalam fail .env aplikasi'
ErrQwenPawAuthDisabled: 'Pengesahan log masuk belum didayakan dalam QwenPaw'
ErrHermesPairingCodeUnavailable: 'Kod pasangan buat sementara waktu tidak wujud dalam Hermes, mungkin disebabkan masalah rangkaian. Sila cuba lagi sebentar nanti.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Apabila dasar kumpulan Feishu ialah senarai benarkan, dasar DM tidak boleh menggunakan kod pasangan.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'Este agente já está vinculado a um site'
ErrAgentWebsiteTypeUnsupported: 'Somente sites proxy ou estáticos podem ser vinculados'
ErrAgentWebsiteInUse: 'Este site já está vinculado a outro agente'
ErrAgentWebsiteUnbindUnsupported: 'Sites implantados em um clique não podem ser desvinculados manualmente'
ErrQwenPawAuthRequest: 'Falha ao atualizar as credenciais do QwenPaw: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'As credenciais atuais do QwenPaw não correspondem ao registro do 1Panel. Redefina a senha do QwenPaw ou restaure as credenciais no arquivo .env do aplicativo'
ErrQwenPawAuthDisabled: 'A autenticação de login não está ativada no QwenPaw'
ErrHermesPairingCodeUnavailable: 'O código de pareamento está temporariamente indisponível no Hermes, possivelmente por causa de rede. Tente novamente mais tarde.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Quando a política de grupo do Feishu é Lista de permissões, a política de DM não pode ser Código de pareamento.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'Этот агент уже связан с сайтом'
ErrAgentWebsiteTypeUnsupported: 'Можно связывать только proxy- или static-сайты'
ErrAgentWebsiteInUse: 'Этот сайт уже связан с другим агентом'
ErrAgentWebsiteUnbindUnsupported: 'Сайты one-click deployment нельзя отвязать вручную'
ErrQwenPawAuthRequest: 'Не удалось обновить учетные данные QwenPaw: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'Текущие учетные данные QwenPaw не совпадают с записью 1Panel. Сбросьте пароль QwenPaw или восстановите учетные данные в файле .env приложения'
ErrQwenPawAuthDisabled: 'В QwenPaw не включена аутентификация при входе'
ErrHermesPairingCodeUnavailable: 'Код сопряжения временно недоступен в Hermes, возможно из-за проблем с сетью. Повторите попытку позже.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Когда групповая политика Feishu — белый список, политика личных сообщений не может быть кодом сопряжения.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: 'Bu ajan zaten bir web sitesine bağlı'
ErrAgentWebsiteTypeUnsupported: 'Yalnızca proxy veya statik web siteleri bağlanabilir'
ErrAgentWebsiteInUse: 'Bu web sitesi zaten başka bir ajana bağlı'
ErrAgentWebsiteUnbindUnsupported: 'Tek tıkla dağıtılan web sitelerinin bağlantısı manuel olarak kaldırılamaz'
ErrQwenPawAuthRequest: 'QwenPaw kimlik bilgileri güncellenemedi: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'Geçerli QwenPaw kimlik bilgileri 1Panel kaydıyla eşleşmiyor. QwenPaw parolasını sıfırlayın veya uygulamanın .env dosyasındaki kimlik bilgilerini geri yükleyin'
ErrQwenPawAuthDisabled: 'QwenPaw oturum açma kimlik doğrulaması etkin değil'
ErrHermesPairingCodeUnavailable: 'Eşleştirme kodu Hermes içinde geçici olarak bulunamıyor; bu durum ağ kaynaklı olabilir. Lütfen daha sonra tekrar deneyin.'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: 'Feishu grup ilkesi izin listesi olduğunda, DM ilkesi eşleştirme kodu olamaz.'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: '該智能體已關聯網站'
ErrAgentWebsiteTypeUnsupported: '只能關聯反向代理或靜態網站'
ErrAgentWebsiteInUse: '該網站已被其他智能體關聯'
ErrAgentWebsiteUnbindUnsupported: '一鍵部署網站不支援手動解綁'
ErrQwenPawAuthRequest: 'QwenPaw 驗證資訊更新失敗: {{ .err }}'
ErrQwenPawAuthOutOfSync: 'QwenPaw 目前憑據與 1Panel 記錄不一致,請先重設 QwenPaw 密碼或還原應用程式 .env 中的憑據'
ErrQwenPawAuthDisabled: 'QwenPaw 尚未啟用登入驗證'
ErrHermesPairingCodeUnavailable: '配對碼在 Hermes 中暫時不存在,可能是由於網路原因,請稍後再試'
ErrHermesFeishuGroupAllowlistRequiresAllowlist: '飛書群組策略為白名單時,私聊策略不能為配對碼'
+3
View File
@@ -93,6 +93,9 @@ ErrAgentWebsiteBound: "该智能体已关联网站"
ErrAgentWebsiteTypeUnsupported: "只能关联反向代理或静态网站"
ErrAgentWebsiteInUse: "该网站已被其他智能体关联"
ErrAgentWebsiteUnbindUnsupported: "一键部署网站不支持手动解绑"
ErrQwenPawAuthRequest: "QwenPaw 认证信息更新失败: {{ .err }}"
ErrQwenPawAuthOutOfSync: "QwenPaw 当前凭据与 1Panel 记录不一致,请先重置 QwenPaw 密码或恢复应用 .env 中的凭据"
ErrQwenPawAuthDisabled: "QwenPaw 尚未启用登录认证"
ErrHermesPairingCodeUnavailable: "配对码在 hermes 中暂时不存在,可能是由于网络原因,请稍后尝试"
ErrHermesFeishuGroupAllowlistRequiresAllowlist: "飞书群组策略为白名单时,私聊策略不能为配对码"
+2 -2
View File
@@ -969,9 +969,9 @@ export namespace AI {
export interface AgentOtherConfigUpdateReq {
agentId: number;
userTimezone: string;
userTimezone?: string;
browserEnabled: boolean;
npmRegistry: string;
npmRegistry?: string;
dashboardUsername?: string;
dashboardPassword?: string;
}
+2
View File
@@ -922,6 +922,8 @@ const message = {
channelAutoRestartHelper: 'Saving will automatically restart the container so the changes take effect.',
dashboardAuthAutoRebuildHelper:
'Saving the username or password will automatically rebuild the container so the changes take effect.',
dashboardAuthImmediateHelper:
'Saving updates the QwenPaw login credentials immediately without restarting the container.',
channelDeleteConfirm: 'Delete the {0} channel configuration?',
apiTypeBaseURLHelper: 'This type sends requests to {0}. Recommended Base URL: {1}',
customImageURLHelper:
+2
View File
@@ -925,6 +925,8 @@ const message = {
'Al guardar, el contenedor se reiniciará automáticamente para que la configuración surta efecto.',
dashboardAuthAutoRebuildHelper:
'Al guardar el usuario o la contraseña, el contenedor se reconstruirá automáticamente para que los cambios surtan efecto.',
dashboardAuthImmediateHelper:
'Al guardar, las credenciales de QwenPaw se actualizan de inmediato sin reiniciar el contenedor.',
channelDeleteConfirm: '¿Eliminar la configuración del canal {0}?',
apiTypeBaseURLHelper: 'Este tipo envía solicitudes a {0}. Base URL recomendada: {1}',
customImageURLHelper:
+2
View File
@@ -910,6 +910,8 @@ const message = {
channelAutoRestartHelper: 'ذخیره به طور خودکار کانتینر را مجدداً راه‌اندازی می‌کند تا تغییرات اعمال شوند.',
dashboardAuthAutoRebuildHelper:
'ذخیره نام کاربری یا رمز عبور، کانتینر را به‌طور خودکار بازسازی می‌کند تا تغییرات اعمال شوند.',
dashboardAuthImmediateHelper:
'با ذخیره‌سازی، اطلاعات ورود QwenPaw بدون راه‌اندازی مجدد کانتینر فوراً به‌روزرسانی می‌شود.',
channelDeleteConfirm: 'پیکربندی کانال {0} حذف شود؟',
apiTypeBaseURLHelper: 'این نوع درخواست‌ها را به {0} ارسال می‌کند. Base URL پیشنهادی: {1}',
apiTypeBaseURLMismatch: 'Base URL فعلی شبیه مسیر {0} است، اما {1} انتخاب شده است. URL پیشنهادی: {2}',
+2
View File
@@ -914,6 +914,8 @@ const message = {
channelAutoRestartHelper: '保存後、設定を反映するためにコンテナが自動で再起動されます。',
dashboardAuthAutoRebuildHelper:
'ユーザー名またはパスワードを保存すると、変更を反映するためにコンテナが自動で再作成されます。',
dashboardAuthImmediateHelper:
'保存すると QwenPaw のログイン認証情報がすぐに更新され、コンテナの再起動は不要です。',
channelDeleteConfirm: '{0} チャンネルの設定を削除しますか?',
apiTypeBaseURLHelper: 'このタイプは最終的に {0} にリクエストします。推奨 Base URL: {1}',
customImageURLHelper:
+2
View File
@@ -904,6 +904,8 @@ const message = {
channelAutoRestartHelper: '저장하면 설정 적용을 위해 컨테이너가 자동으로 재시작됩니다.',
dashboardAuthAutoRebuildHelper:
'사용자 이름 또는 비밀번호를 저장하면 변경 사항 적용을 위해 컨테이너가 자동으로 재생성됩니다.',
dashboardAuthImmediateHelper:
'저장하면 컨테이너를 다시 시작하지 않고 QwenPaw 로그인 자격 증명이 즉시 업데이트됩니다.',
channelDeleteConfirm: '{0} 채널 구성을 삭제하시겠습니까?',
apiTypeBaseURLHelper: '이 유형은 최종적으로 {0}에 요청합니다. 권장 Base URL: {1}',
customImageURLHelper:
+2
View File
@@ -904,6 +904,8 @@ const message = {
channelAutoRestartHelper: 'ການບັນທຶກຈະຣີສະຕາດຄອນເທນເນີອັດຕະໂນມັດເພື່ອໃຫ້ການປ່ຽນແປງມີຜົນ.',
dashboardAuthAutoRebuildHelper:
'ການບັນທຶກຊື່ຜູ້ໃຊ້ ຫຼື ລະຫັດຜ່ານ Dashboard ຈະສ້າງຄອນເທນເນີໃໝ່ອັດຕະໂນມັດເພື່ອໃຫ້ການປ່ຽນແປງມີຜົນ.',
dashboardAuthImmediateHelper:
'ເມື່ອບັນທຶກ ຂໍ້ມູນເຂົ້າລະບົບ QwenPaw ຈະຖືກອັບເດດທັນທີໂດຍບໍ່ຕ້ອງຣີສະຕາດຄອນເທນເນີ.',
channelDeleteConfirm: 'ລຶບການຕັ້ງຄ່າຊ່ອງທາງ {0}?',
apiTypeBaseURLHelper: 'ປະເພດນີ້ຈະສົ່ງການຮ້ອງຂໍໄປທີ່ {0}. Base URL ທີ່ແນະນຳ: {1}',
customImageURLHelper:
+2
View File
@@ -933,6 +933,8 @@ const message = {
'Menyimpan akan memulakan semula bekas secara automatik supaya konfigurasi berkuat kuasa.',
dashboardAuthAutoRebuildHelper:
'Menyimpan nama pengguna atau kata laluan akan membina semula bekas secara automatik supaya perubahan berkuat kuasa.',
dashboardAuthImmediateHelper:
'Apabila disimpan, kelayakan log masuk QwenPaw dikemas kini serta-merta tanpa memulakan semula bekas.',
channelDeleteConfirm: 'Padam konfigurasi saluran {0}?',
apiTypeBaseURLHelper: 'Jenis ini menghantar permintaan ke {0}. Base URL yang disyorkan: {1}',
customImageURLHelper:
+2
View File
@@ -929,6 +929,8 @@ const message = {
'Ao salvar, o contêiner será reiniciado automaticamente para que a configuração entre em vigor.',
dashboardAuthAutoRebuildHelper:
'Ao salvar o usuário ou a senha, o contêiner será reconstruído automaticamente para que as alterações entrem em vigor.',
dashboardAuthImmediateHelper:
'Ao salvar, as credenciais do QwenPaw são atualizadas imediatamente, sem reiniciar o contêiner.',
channelDeleteConfirm: 'Excluir a configuração do canal {0}?',
apiTypeBaseURLHelper: 'Este tipo envia requisições para {0}. Base URL recomendada: {1}',
customImageURLHelper:
+2
View File
@@ -922,6 +922,8 @@ const message = {
'После сохранения контейнер будет автоматически перезапущен, чтобы настройки вступили в силу.',
dashboardAuthAutoRebuildHelper:
'После сохранения имени пользователя или пароля контейнер будет автоматически пересобран, чтобы изменения вступили в силу.',
dashboardAuthImmediateHelper:
'После сохранения учетные данные QwenPaw обновляются сразу, без перезапуска контейнера.',
channelDeleteConfirm: 'Удалить конфигурацию канала {0}?',
apiTypeBaseURLHelper: 'Этот тип отправляет запросы в {0}. Рекомендуемый Base URL: {1}',
customImageURLHelper:
+2
View File
@@ -931,6 +931,8 @@ const message = {
'Kaydettiğinizde ayarların etkili olması için konteyner otomatik olarak yeniden başlatılır.',
dashboardAuthAutoRebuildHelper:
'Kullanıcı adı veya parola kaydedildiğinde değişikliklerin etkili olması için konteyner otomatik olarak yeniden oluşturulur.',
dashboardAuthImmediateHelper:
'Kaydedildiğinde QwenPaw oturum açma bilgileri konteyner yeniden başlatılmadan hemen güncellenir.',
channelDeleteConfirm: '{0} kanal yapılandırması silinsin mi?',
apiTypeBaseURLHelper: 'Bu tür istekleri {0} yoluna gönderir. Önerilen Base URL: {1}',
customImageURLHelper:
+1
View File
@@ -877,6 +877,7 @@ const message = {
scanConnectHelper: '點擊後將在任務日誌中顯示 QR Code,掃碼確認成功後將自動重新啟動容器。',
channelAutoRestartHelper: '保存後將自動重新啟動容器以使設定生效。',
dashboardAuthAutoRebuildHelper: '保存使用者名稱或密碼後將自動重建容器以使設定生效。',
dashboardAuthImmediateHelper: '保存後將立即更新 QwenPaw 登入憑據,無需重新啟動容器。',
channelDeleteConfirm: '確認刪除 {0} 頻道設定?',
apiTypeBaseURLHelper: '目前類型最終請求 {0},Base URL 建議填寫 {1}',
customImageURLHelper: 'Custom 圖片帳號請填寫完整的圖片產生介面 URL,系統不會自動補全路徑,例如:{0}',
+1
View File
@@ -890,6 +890,7 @@ const message = {
scanConnectHelper: '点击后将在任务日志中显示二维码,扫码确认成功后将自动重启容器。',
channelAutoRestartHelper: '保存后将自动重启容器以使配置生效。',
dashboardAuthAutoRebuildHelper: '保存用户名密码后将自动重建容器以使配置生效。',
dashboardAuthImmediateHelper: '保存后将立即更新 QwenPaw 登录凭据,无需重启容器。',
channelDeleteConfirm: '确认删除 {0} 频道配置?',
apiTypeBaseURLHelper: '当前类型最终请求 {0},Base URL 建议填写 {1}',
customImageURLHelper: 'Custom 图片账号请填写完整的图片生成接口 URL,系统不会自动补全路径,例如:{0}',
+4
View File
@@ -26,6 +26,10 @@ export const supportsAgentToken = (agentType: string): boolean => {
return agentType === 'openclaw';
};
export const supportsAgentDashboardAuth = (agentType: string): boolean => {
return agentType === 'hermes-agent' || agentType === 'copaw';
};
export const isOpenclawHTTPSWindowVersion = (version: string): boolean => {
return compareVersion(version, openclawHTTPSVersion) && !compareVersion(version, openclawHTTPVersion);
};
@@ -23,7 +23,7 @@
<el-form-item :label="$t('aiTools.agents.webuiPort')" prop="webUIPort">
<el-input-number v-model="form.webUIPort" :min="1" :max="65535" />
</el-form-item>
<template v-if="form.agentType === 'hermes-agent'">
<template v-if="supportsAgentDashboardAuth(form.agentType)">
<el-form-item :label="$t('commons.login.username')" prop="dashboardUsername">
<el-input v-model="form.dashboardUsername" />
</el-form-item>
@@ -131,6 +131,7 @@ import {
buildDefaultAllowedOrigin,
getAgentProviderDisplayName,
parseAllowedOriginsInput,
supportsAgentDashboardAuth,
validateAllowedOriginsInput,
} from '@/utils/agent';
import { App } from '@/api/interface/app';
@@ -187,8 +188,8 @@ const generateDashboardPassword = () => {
form.dashboardPassword = getRandomStr(8);
};
const ensureHermesDashboardAuth = () => {
if (form.agentType !== 'hermes-agent') {
const ensureDashboardAuth = () => {
if (!supportsAgentDashboardAuth(form.agentType)) {
return;
}
if (!form.dashboardUsername) {
@@ -411,7 +412,7 @@ const handleAgentTypeChange = async () => {
form.provider = '';
form.accountId = undefined as unknown as number;
form.baseURL = '';
ensureHermesDashboardAuth();
ensureDashboardAuth();
if (form.agentType === 'openclaw') {
await loadSystemIP();
allowedOriginsAutoFilled.value = true;
@@ -487,8 +488,8 @@ const submit = async () => {
model: showModelConfig.value ? form.model : undefined,
accountId: showModelConfig.value ? form.accountId : undefined,
token: form.agentType === 'openclaw' ? form.token : undefined,
dashboardUsername: form.agentType === 'hermes-agent' ? form.dashboardUsername : undefined,
dashboardPassword: form.agentType === 'hermes-agent' ? form.dashboardPassword : undefined,
dashboardUsername: supportsAgentDashboardAuth(form.agentType) ? form.dashboardUsername : undefined,
dashboardPassword: supportsAgentDashboardAuth(form.agentType) ? form.dashboardPassword : undefined,
taskID: taskID,
advanced: form.advanced,
containerName: form.containerName,
@@ -536,7 +537,7 @@ const openDrawer = async (agentType?: AI.AgentType) => {
form.agentType = targetType;
setDefaultWebUIPort();
form.token = getRandomStr(32).toLowerCase();
ensureHermesDashboardAuth();
ensureDashboardAuth();
if (form.agentType === 'copaw') {
form.allowedOrigins = '';
lastAutoAllowedOrigins.value = '';
@@ -9,7 +9,7 @@
>
<ChannelsTab ref="channelsRef" :app-version="appVersion" :agent-type="agentType" />
</el-tab-pane>
<el-tab-pane :label="t('aiTools.model.model')" name="model">
<el-tab-pane v-if="supportsAgentModelConfig(agentType)" :label="t('aiTools.model.model')" name="model">
<ModelTab ref="modelRef" @updated="handleModelUpdated" />
</el-tab-pane>
<el-tab-pane v-if="agentType === 'openclaw'" :label="t('aiTools.agents.agentRoleTab')" name="agent">
@@ -26,7 +26,7 @@
<PluginsTab ref="pluginsRef" />
</el-tab-pane>
<el-tab-pane
v-if="agentType === 'openclaw' || agentType === 'hermes-agent'"
v-if="agentType === 'openclaw' || agentType === 'hermes-agent' || agentType === 'copaw'"
:label="t('file.setting')"
name="settings"
>
@@ -48,6 +48,7 @@ import AgentTab from './tabs/agents/index.vue';
import SkillsTab from './tabs/skills.vue';
import PluginsTab from './tabs/plugins.vue';
import SettingsTab from './tabs/settings.vue';
import { supportsAgentModelConfig } from '@/utils/agent';
const { t } = useI18n();
const emit = defineEmits(['updated']);
@@ -165,13 +166,13 @@ const openDrawer = async (agent: AI.AgentItem) => {
configPath.value = agent.configPath;
agentType.value = agent.agentType;
header.value = `${agent.name} - ${t('menu.config')}`;
activeTab.value = agent.agentType === 'openclaw' || agent.agentType === 'hermes-agent' ? 'channels' : 'model';
activeTab.value = agent.agentType === 'copaw' ? 'settings' : 'channels';
open.value = true;
if (agent.agentType === 'openclaw' || agent.agentType === 'hermes-agent') {
await loadChannels();
return;
}
await loadModel();
await loadSettings();
};
defineExpose({
@@ -9,10 +9,10 @@
</el-select>
<span class="input-help">{{ t('aiTools.agents.npmRegistryHelper') }}</span>
</el-form-item>
<el-form-item :label="t('aiTools.agents.timeZone')" prop="userTimezone">
<el-form-item v-if="agentType !== 'copaw'" :label="t('aiTools.agents.timeZone')" prop="userTimezone">
<el-input v-model="form.userTimezone" />
</el-form-item>
<template v-if="agentType === 'hermes-agent'">
<template v-if="supportsAgentDashboardAuth(agentType)">
<el-form-item :label="t('commons.login.username')" prop="dashboardUsername">
<el-input v-model="form.dashboardUsername" />
</el-form-item>
@@ -33,7 +33,9 @@
{{
agentType === 'hermes-agent'
? t('aiTools.agents.dashboardAuthAutoRebuildHelper')
: t('aiTools.agents.channelAutoRestartHelper')
: agentType === 'copaw'
? t('aiTools.agents.dashboardAuthImmediateHelper')
: t('aiTools.agents.channelAutoRestartHelper')
}}
</span>
</el-form>
@@ -47,6 +49,7 @@ import { Rules } from '@/global/form-rules';
import { AI } from '@/api/interface/ai';
import { getAgentOtherConfig, updateAgentOtherConfig } from '@/api/modules/ai';
import { MsgSuccess } from '@/utils/message';
import { supportsAgentDashboardAuth } from '@/utils/agent';
const { t } = useI18n();
const loading = ref(false);
@@ -71,6 +74,16 @@ const npmRegistryOptions = [
'https://repo.huaweicloud.com/repository/npm/',
];
const resetForm = () => {
Object.assign(form, {
userTimezone: '',
browserEnabled: true,
npmRegistry: defaultNPMRegistry,
dashboardUsername: 'admin',
dashboardPassword: '',
});
};
const validateNPMRegistry = (_rule: any, value: string, callback: (error?: Error) => void) => {
if (!value) {
callback(new Error(t('commons.rule.requiredInput')));
@@ -93,11 +106,12 @@ const rules = reactive({
const load = async (params: { agentId: number; agentType: AI.AgentType }) => {
agentId.value = params.agentId;
agentType.value = params.agentType;
resetForm();
loading.value = true;
try {
const res = await getAgentOtherConfig({ agentId: params.agentId });
Object.assign(form, res.data || {});
if (params.agentType === 'hermes-agent' && !form.dashboardUsername) {
if (supportsAgentDashboardAuth(params.agentType) && !form.dashboardUsername) {
form.dashboardUsername = 'admin';
}
} finally {
@@ -114,11 +128,11 @@ const saveConfig = async () => {
try {
await updateAgentOtherConfig({
agentId: agentId.value,
userTimezone: form.userTimezone,
userTimezone: agentType.value === 'copaw' ? undefined : form.userTimezone,
browserEnabled: form.browserEnabled,
npmRegistry: agentType.value === 'openclaw' ? form.npmRegistry : defaultNPMRegistry,
dashboardUsername: agentType.value === 'hermes-agent' ? form.dashboardUsername : undefined,
dashboardPassword: agentType.value === 'hermes-agent' ? form.dashboardPassword : undefined,
dashboardUsername: supportsAgentDashboardAuth(agentType.value) ? form.dashboardUsername : undefined,
dashboardPassword: supportsAgentDashboardAuth(agentType.value) ? form.dashboardPassword : undefined,
});
MsgSuccess(t('aiTools.agents.saveSuccess'));
} finally {
+4 -2
View File
@@ -173,7 +173,7 @@
</el-button>
</el-space>
<el-space
v-else-if="row.agentType === 'hermes-agent'"
v-else-if="supportsAgentDashboardAuth(row.agentType)"
direction="vertical"
alignment="start"
:size="2"
@@ -303,6 +303,7 @@ import {
getOpenclawAccessScheme,
supportsAgentModelConfig,
supportsAgentToken,
supportsAgentDashboardAuth,
} from '@/utils/agent';
import { routerToFileWithPath } from '@/utils/router';
import { listDomains } from '@/api/modules/website';
@@ -356,7 +357,8 @@ const buttons = [
{
label: i18n.global.t('menu.config'),
click: (row: AI.AgentItem) => openConfig(row),
show: (row: AI.AgentItem) => supportsAgentModelConfig(row.agentType),
show: (row: AI.AgentItem) =>
supportsAgentModelConfig(row.agentType) || supportsAgentDashboardAuth(row.agentType),
disabled: (row: AI.AgentItem) => row.status !== 'Running',
},
{