mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-20 16:00:51 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cdc4f476a |
@@ -9,8 +9,7 @@ type AgentCreateReq struct {
|
|||||||
WebUIPort int `json:"webUIPort" validate:"required,min=1,max=65535"`
|
WebUIPort int `json:"webUIPort" validate:"required,min=1,max=65535"`
|
||||||
BridgePort int `json:"bridgePort"`
|
BridgePort int `json:"bridgePort"`
|
||||||
AllowedOrigins []string `json:"allowedOrigins"`
|
AllowedOrigins []string `json:"allowedOrigins"`
|
||||||
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent deepseek-harness octop"`
|
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw hermes-agent"`
|
||||||
AccessHost string `json:"accessHost"`
|
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
AccountID uint `json:"accountId"`
|
AccountID uint `json:"accountId"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -111,7 +110,6 @@ type AgentBatchOperateResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AgentItem struct {
|
type AgentItem struct {
|
||||||
AccessHost string `json:"accessHost"`
|
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
|
|||||||
@@ -142,9 +142,6 @@ const (
|
|||||||
|
|
||||||
func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
||||||
agentType := req.AgentType
|
agentType := req.AgentType
|
||||||
if err := validateNativeAgentCreate(req); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := checkPortExist(req.WebUIPort); err != nil {
|
if err := checkPortExist(req.WebUIPort); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -240,8 +237,6 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
}
|
}
|
||||||
} else if agentType == constant.AppCopaw {
|
} else if agentType == constant.AppCopaw {
|
||||||
dashboardAuth = normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword)
|
dashboardAuth = normalizeAgentDashboardAuth(req.DashboardUsername, req.DashboardPassword)
|
||||||
} else if agentType == constant.AppDeepSeekHarness || agentType == constant.AppOctop {
|
|
||||||
dashboardAuth = agentDashboardAuth{Username: req.DashboardUsername, Password: req.DashboardPassword}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
@@ -250,12 +245,6 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
|||||||
constant.HostIP: "",
|
constant.HostIP: "",
|
||||||
}
|
}
|
||||||
setAgentWebUIParams(params, agentType, detail.Version, req.WebUIPort)
|
setAgentWebUIParams(params, agentType, detail.Version, req.WebUIPort)
|
||||||
if agentType == constant.AppDeepSeekHarness {
|
|
||||||
params["HTTPS_ACCESS_HOST"] = req.AccessHost
|
|
||||||
}
|
|
||||||
if agentType == constant.AppOctop {
|
|
||||||
params["OCTOP_LOG_LEVEL"] = "info"
|
|
||||||
}
|
|
||||||
if agentType == constant.AppOpenclaw {
|
if agentType == constant.AppOpenclaw {
|
||||||
if allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != "" {
|
if allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != "" {
|
||||||
params["ALLOWED_ORIGIN"] = allowedOrigin
|
params["ALLOWED_ORIGIN"] = allowedOrigin
|
||||||
@@ -749,7 +738,7 @@ func requiresAgentAccount(agentType string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setAgentWebUIParams(params map[string]interface{}, agentType, appVersion string, webUIPort int) {
|
func setAgentWebUIParams(params map[string]interface{}, agentType, appVersion string, webUIPort int) {
|
||||||
if agentType == constant.AppDeepSeekHarness || agentType == constant.AppOpenclaw && isOpenclawHTTPSWindowVersion(appVersion) {
|
if agentType == constant.AppOpenclaw && isOpenclawHTTPSWindowVersion(appVersion) {
|
||||||
params["PANEL_APP_PORT_HTTPS"] = webUIPort
|
params["PANEL_APP_PORT_HTTPS"] = webUIPort
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
||||||
)
|
|
||||||
|
|
||||||
var deepSeekUsernamePattern = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
|
|
||||||
var deepSeekHostLabelPattern = regexp.MustCompile(`^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$`)
|
|
||||||
|
|
||||||
func isDeepSeekAccessHost(value string) bool {
|
|
||||||
if ip := net.ParseIP(value); ip != nil {
|
|
||||||
return ip.To4() != nil && !strings.Contains(value, ":")
|
|
||||||
}
|
|
||||||
if value == "" || len(value) > 253 || strings.Trim(value, "0123456789.") == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, label := range strings.Split(value, ".") {
|
|
||||||
if !deepSeekHostLabelPattern.MatchString(label) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateNativeAgentCreate(req dto.AgentCreateReq) error {
|
|
||||||
if req.AgentType != constant.AppDeepSeekHarness && req.AgentType != constant.AppOctop {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(req.DashboardUsername) == "" || strings.ContainsAny(req.DashboardUsername, "\r\n\x00") {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardUsername", nil)
|
|
||||||
}
|
|
||||||
if strings.ContainsAny(req.DashboardPassword, "\r\n\x00") || req.DashboardPassword != strings.TrimSpace(req.DashboardPassword) {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardPassword", nil)
|
|
||||||
}
|
|
||||||
if req.AgentType == constant.AppDeepSeekHarness {
|
|
||||||
if !isDeepSeekAccessHost(req.AccessHost) {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "accessHost", nil)
|
|
||||||
}
|
|
||||||
if !deepSeekUsernamePattern.MatchString(req.DashboardUsername) {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardUsername", nil)
|
|
||||||
}
|
|
||||||
if utf8.RuneCountInString(req.DashboardPassword) < 12 {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardPassword", nil)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
hasLetter, hasDigit := false, false
|
|
||||||
for _, char := range req.DashboardPassword {
|
|
||||||
hasLetter = hasLetter || unicode.IsLetter(char)
|
|
||||||
hasDigit = hasDigit || unicode.IsDigit(char)
|
|
||||||
}
|
|
||||||
if utf8.RuneCountInString(req.DashboardPassword) < 8 || !hasLetter || !hasDigit {
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardPassword", nil)
|
|
||||||
}
|
|
||||||
switch strings.ToLower(req.DashboardPassword) {
|
|
||||||
case "password", "password1", "password12", "password123", "12345678", "123456789", "qwerty123",
|
|
||||||
"admin123", "welcome1", "letmein1", "changeme1", "octop123", "abc12345", "iloveyou1":
|
|
||||||
return buserr.WithDetail("ErrInvalidParams", "dashboardPassword", nil)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -415,15 +415,6 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
|
|||||||
} else {
|
} else {
|
||||||
item.WebUIPort = appInstall.HttpPort
|
item.WebUIPort = appInstall.HttpPort
|
||||||
}
|
}
|
||||||
if agentType == constant.AppDeepSeekHarness {
|
|
||||||
item.WebUIPort = appInstall.HttpsPort
|
|
||||||
if values, err := readAgentEnvMap(appInstall.GetEnvPath()); err == nil {
|
|
||||||
accessHost := values["HTTPS_ACCESS_HOST"]
|
|
||||||
if isDeepSeekAccessHost(accessHost) {
|
|
||||||
item.AccessHost = accessHost
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.Path = appInstall.GetPath()
|
item.Path = appInstall.GetPath()
|
||||||
item.Status = appInstall.Status
|
item.Status = appInstall.Status
|
||||||
item.Message = appInstall.Message
|
item.Message = appInstall.Message
|
||||||
@@ -451,8 +442,7 @@ func localizedAgentProviderName(provider string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isAgentAppKey(appKey string) bool {
|
func isAgentAppKey(appKey string) bool {
|
||||||
return appKey == constant.AppOpenclaw || appKey == constant.AppCopaw || appKey == constant.AppHermesAgent ||
|
return appKey == constant.AppOpenclaw || appKey == constant.AppCopaw || appKey == constant.AppHermesAgent
|
||||||
appKey == constant.AppDeepSeekHarness || appKey == constant.AppOctop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOpenclawLegacyHTTPVersion(version string) bool {
|
func isOpenclawLegacyHTTPVersion(version string) bool {
|
||||||
@@ -1651,10 +1641,6 @@ func agentDashboardAuthEnvKeys(agentType string) (string, string, bool) {
|
|||||||
return hermesDashboardUsernameEnvKey, hermesDashboardPasswordEnvKey, true
|
return hermesDashboardUsernameEnvKey, hermesDashboardPasswordEnvKey, true
|
||||||
case constant.AppCopaw:
|
case constant.AppCopaw:
|
||||||
return qwenPawAuthUsernameEnvKey, qwenPawAuthPasswordEnvKey, true
|
return qwenPawAuthUsernameEnvKey, qwenPawAuthPasswordEnvKey, true
|
||||||
case constant.AppDeepSeekHarness:
|
|
||||||
return "DSH_AUTH_USERNAME", "DSH_AUTH_PASSWORD", true
|
|
||||||
case constant.AppOctop:
|
|
||||||
return "OCTOP_ADMIN_USERNAME", "OCTOP_DEFAULT_PASSWORD", true
|
|
||||||
default:
|
default:
|
||||||
return "", "", false
|
return "", "", false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ const (
|
|||||||
AppOpenclaw = "openclaw"
|
AppOpenclaw = "openclaw"
|
||||||
AppCopaw = "copaw"
|
AppCopaw = "copaw"
|
||||||
AppHermesAgent = "hermes-agent"
|
AppHermesAgent = "hermes-agent"
|
||||||
AppDeepSeekHarness = "deepseek-harness"
|
|
||||||
AppOctop = "octop"
|
|
||||||
AppMysql = "mysql"
|
AppMysql = "mysql"
|
||||||
AppMariaDB = "mariadb"
|
AppMariaDB = "mariadb"
|
||||||
AppPostgresql = "postgresql"
|
AppPostgresql = "postgresql"
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ func agentDBMigrations() []*gormigrate.Migration {
|
|||||||
migrations.AddMonitorSettings,
|
migrations.AddMonitorSettings,
|
||||||
migrations.AddVLLMRequestMetrics,
|
migrations.AddVLLMRequestMetrics,
|
||||||
migrations.AddVLLMSampleInterval,
|
migrations.AddVLLMSampleInterval,
|
||||||
migrations.AddNativeAgentInstalls,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package migrations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/global"
|
|
||||||
"github.com/go-gormigrate/gormigrate/v2"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AddNativeAgentInstalls = &gormigrate.Migration{
|
|
||||||
ID: "20261020-add-deepseek-harness-octop-agent-installs",
|
|
||||||
Migrate: func(tx *gorm.DB) error {
|
|
||||||
var installs []struct {
|
|
||||||
model.AppInstall
|
|
||||||
AgentType string
|
|
||||||
}
|
|
||||||
if err := tx.Table("app_installs").
|
|
||||||
Select("app_installs.*, apps.key AS agent_type").
|
|
||||||
Joins("JOIN apps ON apps.id = app_installs.app_id").
|
|
||||||
Where("apps.key IN ?", []string{constant.AppDeepSeekHarness, constant.AppOctop}).
|
|
||||||
Where("NOT EXISTS (SELECT 1 FROM agents WHERE agents.app_install_id = app_installs.id)").
|
|
||||||
Order("app_installs.id").
|
|
||||||
Scan(&installs).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, install := range installs {
|
|
||||||
var count int64
|
|
||||||
if err := tx.Model(&model.Agent{}).Where("LOWER(name) = LOWER(?)", install.Name).Count(&count).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
global.LOG.Warnf("skip agent registration for app install %d: name already exists", install.ID)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
agent := model.Agent{
|
|
||||||
BaseModel: model.BaseModel{CreatedAt: install.CreatedAt, UpdatedAt: install.UpdatedAt},
|
|
||||||
Name: install.Name, AgentType: install.AgentType, AppInstallID: install.ID,
|
|
||||||
Status: install.Status, Message: install.Message,
|
|
||||||
}
|
|
||||||
if err := tx.Create(&agent).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "An unrevoked API key with this name already exists"
|
|||||||
ErrAPIKeyNotFound: "The API key does not exist or does not belong to the current user"
|
ErrAPIKeyNotFound: "The API key does not exist or does not belong to the current user"
|
||||||
ErrAPIKeyAppBindingDisabled: "APP QR binding is not allowed for this API key"
|
ErrAPIKeyAppBindingDisabled: "APP QR binding is not allowed for this API key"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "Unable to verify API key terminal authorization support. Check the node connection and upgrade its Agent before retrying."
|
ErrAPIKeyTerminalUpgradeRequired: "Unable to verify API key terminal authorization support. Check the node connection and upgrade its Agent before retrying."
|
||||||
LocalAppSyncDispatch: "Dispatch local app sync"
|
|
||||||
LocalAppSyncDispatchNode: "Dispatch local app sync to {{.node}} (node task: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "Local app sync across nodes is disabled"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "Ya existe una clave API no revocada con este nombre"
|
|||||||
ErrAPIKeyNotFound: "La clave API no existe o no pertenece al usuario actual"
|
ErrAPIKeyNotFound: "La clave API no existe o no pertenece al usuario actual"
|
||||||
ErrAPIKeyAppBindingDisabled: "Esta clave API no permite vincular la APP mediante código QR"
|
ErrAPIKeyAppBindingDisabled: "Esta clave API no permite vincular la APP mediante código QR"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "No se pudo verificar la autorización de terminal con clave API. Compruebe la conexión del nodo y actualice su Agent antes de reintentar."
|
ErrAPIKeyTerminalUpgradeRequired: "No se pudo verificar la autorización de terminal con clave API. Compruebe la conexión del nodo y actualice su Agent antes de reintentar."
|
||||||
LocalAppSyncDispatch: "Enviar sincronización de apps locales"
|
|
||||||
LocalAppSyncDispatchNode: "Enviar sincronización de apps locales a {{.node}} (tarea del nodo: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "La sincronización de apps locales entre nodos está desactivada"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "یک کلید API لغونشده با این نام از
|
|||||||
ErrAPIKeyNotFound: "کلید API وجود ندارد یا متعلق به کاربر فعلی نیست"
|
ErrAPIKeyNotFound: "کلید API وجود ندارد یا متعلق به کاربر فعلی نیست"
|
||||||
ErrAPIKeyAppBindingDisabled: "اتصال برنامه با کد QR برای این کلید API مجاز نیست"
|
ErrAPIKeyAppBindingDisabled: "اتصال برنامه با کد QR برای این کلید API مجاز نیست"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "امکان بررسی پشتیبانی از مجوزدهی ترمینال با کلید API وجود ندارد. اتصال گره را بررسی و Agent آن را ارتقا دهید، سپس دوباره تلاش کنید."
|
ErrAPIKeyTerminalUpgradeRequired: "امکان بررسی پشتیبانی از مجوزدهی ترمینال با کلید API وجود ندارد. اتصال گره را بررسی و Agent آن را ارتقا دهید، سپس دوباره تلاش کنید."
|
||||||
LocalAppSyncDispatch: "ارسال درخواست همگامسازی برنامههای محلی"
|
|
||||||
LocalAppSyncDispatchNode: "ارسال درخواست همگامسازی به {{.node}} (وظیفه گره: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "همگامسازی برنامههای محلی بین گرهها غیرفعال است"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "同じ名前の未失効 API キーが既に存在しま
|
|||||||
ErrAPIKeyNotFound: "API キーが存在しないか、現在のユーザーに属していません"
|
ErrAPIKeyNotFound: "API キーが存在しないか、現在のユーザーに属していません"
|
||||||
ErrAPIKeyAppBindingDisabled: "この API キーでは APP の QR コード連携が許可されていません"
|
ErrAPIKeyAppBindingDisabled: "この API キーでは APP の QR コード連携が許可されていません"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "API キーによるターミナル認証への対応を確認できません。ノードの接続を確認し、Agent を更新して再試行してください。"
|
ErrAPIKeyTerminalUpgradeRequired: "API キーによるターミナル認証への対応を確認できません。ノードの接続を確認し、Agent を更新して再試行してください。"
|
||||||
LocalAppSyncDispatch: "ローカルアプリ同期要求の送信"
|
|
||||||
LocalAppSyncDispatchNode: "{{.node}} にローカルアプリ同期要求を送信(ノードタスク: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "ノード間のローカルアプリ同期は無効です"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "같은 이름의 폐기되지 않은 API Key가 이미 있
|
|||||||
ErrAPIKeyNotFound: "API Key가 없거나 현재 사용자 소유가 아닙니다"
|
ErrAPIKeyNotFound: "API Key가 없거나 현재 사용자 소유가 아닙니다"
|
||||||
ErrAPIKeyAppBindingDisabled: "이 API Key는 APP QR 코드 연결을 허용하지 않습니다"
|
ErrAPIKeyAppBindingDisabled: "이 API Key는 APP QR 코드 연결을 허용하지 않습니다"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "API Key 터미널 인증 지원 여부를 확인할 수 없습니다. 노드 연결을 확인하고 Agent를 업그레이드한 후 다시 시도하세요."
|
ErrAPIKeyTerminalUpgradeRequired: "API Key 터미널 인증 지원 여부를 확인할 수 없습니다. 노드 연결을 확인하고 Agent를 업그레이드한 후 다시 시도하세요."
|
||||||
LocalAppSyncDispatch: "로컬 앱 동기화 요청 전송"
|
|
||||||
LocalAppSyncDispatchNode: "{{.node}}에 로컬 앱 동기화 요청 전송 (노드 작업: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "노드 간 로컬 앱 동기화가 비활성화되어 있습니다"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "ມີ API Key ທີ່ຍັງບໍ່ຖືກຖອ
|
|||||||
ErrAPIKeyNotFound: "ບໍ່ພົບ API Key ຫຼືບໍ່ແມ່ນຂອງຜູ້ໃຊ້ປັດຈຸບັນ"
|
ErrAPIKeyNotFound: "ບໍ່ພົບ API Key ຫຼືບໍ່ແມ່ນຂອງຜູ້ໃຊ້ປັດຈຸບັນ"
|
||||||
ErrAPIKeyAppBindingDisabled: "API Key ນີ້ບໍ່ອະນຸຍາດໃຫ້ຜູກ APP ຜ່ານລະຫັດ QR"
|
ErrAPIKeyAppBindingDisabled: "API Key ນີ້ບໍ່ອະນຸຍາດໃຫ້ຜູກ APP ຜ່ານລະຫັດ QR"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "ບໍ່ສາມາດຢືນຢັນການຮອງຮັບການອະນຸຍາດເທີມິນອນດ້ວຍ API Key. ກວດສອບການເຊື່ອມຕໍ່ໂນດ ແລະອັບເກຣດ Agent ກ່ອນລອງອີກ."
|
ErrAPIKeyTerminalUpgradeRequired: "ບໍ່ສາມາດຢືນຢັນການຮອງຮັບການອະນຸຍາດເທີມິນອນດ້ວຍ API Key. ກວດສອບການເຊື່ອມຕໍ່ໂນດ ແລະອັບເກຣດ Agent ກ່ອນລອງອີກ."
|
||||||
LocalAppSyncDispatch: "ສົ່ງຄຳຂໍຊິງຄ໌ແອັບໃນເຄື່ອງ"
|
|
||||||
LocalAppSyncDispatchNode: "ສົ່ງຄຳຂໍຊິງຄ໌ໄປຫາ {{.node}} (ວຽກຂອງໂນດ: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "ການຊິງຄ໌ແອັບລະຫວ່າງໂນດຍັງບໍ່ໄດ້ເປີດ"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "Kunci API yang belum dibatalkan dengan nama ini sudah wuju
|
|||||||
ErrAPIKeyNotFound: "Kunci API tidak wujud atau bukan milik pengguna semasa"
|
ErrAPIKeyNotFound: "Kunci API tidak wujud atau bukan milik pengguna semasa"
|
||||||
ErrAPIKeyAppBindingDisabled: "Pemautan APP melalui kod QR tidak dibenarkan untuk kunci API ini"
|
ErrAPIKeyAppBindingDisabled: "Pemautan APP melalui kod QR tidak dibenarkan untuk kunci API ini"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "Sokongan pengesahan terminal melalui kunci API tidak dapat disahkan. Semak sambungan nod dan naik taraf Agent sebelum mencuba lagi."
|
ErrAPIKeyTerminalUpgradeRequired: "Sokongan pengesahan terminal melalui kunci API tidak dapat disahkan. Semak sambungan nod dan naik taraf Agent sebelum mencuba lagi."
|
||||||
LocalAppSyncDispatch: "Hantar penyegerakan aplikasi tempatan"
|
|
||||||
LocalAppSyncDispatchNode: "Hantar penyegerakan aplikasi tempatan ke {{.node}} (tugas nod: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "Penyegerakan aplikasi tempatan antara nod dinyahdayakan"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "Já existe uma chave API não revogada com esse nome"
|
|||||||
ErrAPIKeyNotFound: "A chave API não existe ou não pertence ao usuário atual"
|
ErrAPIKeyNotFound: "A chave API não existe ou não pertence ao usuário atual"
|
||||||
ErrAPIKeyAppBindingDisabled: "Esta chave API não permite vincular o APP por código QR"
|
ErrAPIKeyAppBindingDisabled: "Esta chave API não permite vincular o APP por código QR"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "Não foi possível verificar o suporte à autorização de terminal por chave API. Verifique a conexão do nó e atualize o Agent antes de tentar novamente."
|
ErrAPIKeyTerminalUpgradeRequired: "Não foi possível verificar o suporte à autorização de terminal por chave API. Verifique a conexão do nó e atualize o Agent antes de tentar novamente."
|
||||||
LocalAppSyncDispatch: "Enviar sincronização de apps locais"
|
|
||||||
LocalAppSyncDispatchNode: "Enviar sincronização de apps locais para {{.node}} (tarefa do nó: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "A sincronização de apps locais entre nós está desativada"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "Неотозванный ключ API с таким име
|
|||||||
ErrAPIKeyNotFound: "Ключ API не существует или не принадлежит текущему пользователю"
|
ErrAPIKeyNotFound: "Ключ API не существует или не принадлежит текущему пользователю"
|
||||||
ErrAPIKeyAppBindingDisabled: "Для этого ключа API привязка приложения через QR-код запрещена"
|
ErrAPIKeyAppBindingDisabled: "Для этого ключа API привязка приложения через QR-код запрещена"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "Не удалось проверить поддержку авторизации терминала по ключу API. Проверьте подключение узла, обновите его Agent и повторите попытку."
|
ErrAPIKeyTerminalUpgradeRequired: "Не удалось проверить поддержку авторизации терминала по ключу API. Проверьте подключение узла, обновите его Agent и повторите попытку."
|
||||||
LocalAppSyncDispatch: "Отправка запросов синхронизации локальных приложений"
|
|
||||||
LocalAppSyncDispatchNode: "Отправка запроса синхронизации узлу {{.node}} (задача узла: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "Синхронизация локальных приложений между узлами отключена"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "Bu adda iptal edilmemiş bir API anahtarı zaten var"
|
|||||||
ErrAPIKeyNotFound: "API anahtarı mevcut değil veya geçerli kullanıcıya ait değil"
|
ErrAPIKeyNotFound: "API anahtarı mevcut değil veya geçerli kullanıcıya ait değil"
|
||||||
ErrAPIKeyAppBindingDisabled: "Bu API anahtarı için QR koduyla APP bağlantısına izin verilmiyor"
|
ErrAPIKeyAppBindingDisabled: "Bu API anahtarı için QR koduyla APP bağlantısına izin verilmiyor"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "API anahtarıyla terminal yetkilendirme desteği doğrulanamadı. Düğüm bağlantısını kontrol edip Agent sürümünü yükselttikten sonra tekrar deneyin."
|
ErrAPIKeyTerminalUpgradeRequired: "API anahtarıyla terminal yetkilendirme desteği doğrulanamadı. Düğüm bağlantısını kontrol edip Agent sürümünü yükselttikten sonra tekrar deneyin."
|
||||||
LocalAppSyncDispatch: "Yerel uygulama eşitleme isteği gönder"
|
|
||||||
LocalAppSyncDispatchNode: "{{.node}} düğümüne yerel uygulama eşitleme isteği gönder (düğüm görevi: {{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "Düğümler arası yerel uygulama eşitleme devre dışı"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "已存在同名且未撤銷的 API Key"
|
|||||||
ErrAPIKeyNotFound: "API Key 不存在或不屬於目前使用者"
|
ErrAPIKeyNotFound: "API Key 不存在或不屬於目前使用者"
|
||||||
ErrAPIKeyAppBindingDisabled: "此 API Key 未允許 APP 掃碼綁定"
|
ErrAPIKeyAppBindingDisabled: "此 API Key 未允許 APP 掃碼綁定"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "無法確認節點支援 API Key 終端授權,請檢查節點連線並升級 Agent 後重試。"
|
ErrAPIKeyTerminalUpgradeRequired: "無法確認節點支援 API Key 終端授權,請檢查節點連線並升級 Agent 後重試。"
|
||||||
LocalAppSyncDispatch: "下發本機應用同步"
|
|
||||||
LocalAppSyncDispatchNode: "向節點 {{.node}} 下發本機應用同步(節點任務:{{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "尚未啟用本機應用跨節點同步"
|
|
||||||
|
|||||||
@@ -499,6 +499,3 @@ ErrAPIKeyNameExists: "已存在同名的未撤销 API Key"
|
|||||||
ErrAPIKeyNotFound: "API Key 不存在或不属于当前用户"
|
ErrAPIKeyNotFound: "API Key 不存在或不属于当前用户"
|
||||||
ErrAPIKeyAppBindingDisabled: "此 API Key 未允许 APP 扫码绑定"
|
ErrAPIKeyAppBindingDisabled: "此 API Key 未允许 APP 扫码绑定"
|
||||||
ErrAPIKeyTerminalUpgradeRequired: "无法确认节点支持 API Key 终端授权,请检查节点连接并升级 Agent 后重试。"
|
ErrAPIKeyTerminalUpgradeRequired: "无法确认节点支持 API Key 终端授权,请检查节点连接并升级 Agent 后重试。"
|
||||||
LocalAppSyncDispatch: "下发本地应用同步"
|
|
||||||
LocalAppSyncDispatchNode: "向节点 {{.node}} 下发本地应用同步(节点任务:{{.taskID}})"
|
|
||||||
ErrLocalAppSyncDisabled: "尚未开启本地应用跨节点同步"
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ReqPage } from '.';
|
import { ReqPage } from '.';
|
||||||
|
|
||||||
export namespace AI {
|
export namespace AI {
|
||||||
export type AgentType = 'openclaw' | 'copaw' | 'hermes-agent' | 'deepseek-harness' | 'octop';
|
export type AgentType = 'openclaw' | 'copaw' | 'hermes-agent';
|
||||||
|
|
||||||
export interface OllamaModelInfo {
|
export interface OllamaModelInfo {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -423,7 +423,6 @@ export namespace AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentCreateReq {
|
export interface AgentCreateReq {
|
||||||
accessHost?: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
remark: string;
|
remark: string;
|
||||||
appVersion: string;
|
appVersion: string;
|
||||||
@@ -451,7 +450,6 @@ export namespace AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentItem {
|
export interface AgentItem {
|
||||||
accessHost: string;
|
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
remark: string;
|
remark: string;
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { ReqPage, CommonModel } from '.';
|
import { ReqPage, CommonModel } from '.';
|
||||||
|
|
||||||
export namespace App {
|
export namespace App {
|
||||||
export interface LocalAppSyncConfig {
|
|
||||||
enabled: boolean;
|
|
||||||
}
|
|
||||||
export interface App extends CommonModel {
|
export interface App extends CommonModel {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
|||||||
@@ -144,15 +144,3 @@ export function getAppIconUrl(appKey: string, node?: string): string {
|
|||||||
export const installAppToNodes = (param: App.InstallAppToNodes) => {
|
export const installAppToNodes = (param: App.InstallAppToNodes) => {
|
||||||
return http.post(`/core/xpack/sync/app/install`, param);
|
return http.post(`/core/xpack/sync/app/install`, param);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLocalAppSyncConfig = () => {
|
|
||||||
return http.get<App.LocalAppSyncConfig>('/core/xpack/settings/app/local-sync');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateLocalAppSyncConfig = (config: App.LocalAppSyncConfig) => {
|
|
||||||
return http.post('/core/xpack/settings/app/local-sync', config);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const dispatchLocalAppSync = (req: App.AppStoreSync) => {
|
|
||||||
return http.post('/core/xpack/settings/app/local-sync/dispatch', req);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import http from '@/api';
|
import http from '@/api';
|
||||||
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
import { ResPage, SearchWithPage } from '../interface';
|
import { ResPage, SearchWithPage } from '../interface';
|
||||||
import { Container } from '../interface/container';
|
import { Container } from '../interface/container';
|
||||||
import { TimeoutEnum } from '@/enums/http-enum';
|
import { TimeoutEnum } from '@/enums/http-enum';
|
||||||
@@ -23,10 +24,11 @@ export const loadContainerUsers = (name: string) => {
|
|||||||
export const listContainerFiles = (params: Container.ContainerFileReq) => {
|
export const listContainerFiles = (params: Container.ContainerFileReq) => {
|
||||||
return http.post<Array<Container.ContainerFileInfo>>(`/containers/files/search`, params, TimeoutEnum.T_40S);
|
return http.post<Array<Container.ContainerFileInfo>>(`/containers/files/search`, params, TimeoutEnum.T_40S);
|
||||||
};
|
};
|
||||||
export const uploadContainerFile = (params: FormData) => {
|
export const uploadContainerFile = (params: FormData, config?: AxiosRequestConfig & { skipErrorMessage?: boolean }) => {
|
||||||
return http.upload(`/containers/files/upload`, params, {
|
return http.upload(`/containers/files/upload`, params, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
timeout: TimeoutEnum.T_5M,
|
timeout: TimeoutEnum.T_5M,
|
||||||
|
...config,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data', ...config?.headers },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const getContainerFileContent = (params: Container.ContainerFileReq) => {
|
export const getContainerFileContent = (params: Container.ContainerFileReq) => {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -773,15 +773,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
"Enter the IPv4 address or hostname used in your browser, without a scheme, port or path. HTTPS uses the application's local certificate.",
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Username: letters, numbers, dot, underscore or hyphen. Password: at least 12 characters, without leading/trailing spaces or line breaks.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Password: at least 8 characters with letters and digits; avoid common passwords. If rejected during initialization, check the application logs for setup instructions.',
|
|
||||||
initialCredentials: "Initial credentials; use the application's current password if changed.",
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'The existing installation has no compatible HTTPS access address or port. Check its application settings.',
|
|
||||||
agent: 'Agent',
|
agent: 'Agent',
|
||||||
account: 'Model Account',
|
account: 'Model Account',
|
||||||
batchOperation: 'Batch Operation',
|
batchOperation: 'Batch Operation',
|
||||||
@@ -3701,7 +3692,6 @@ const message = {
|
|||||||
link: 'Link',
|
link: 'Link',
|
||||||
showCurrentArch: 'Architecture',
|
showCurrentArch: 'Architecture',
|
||||||
syncLocalApp: 'Sync Local App',
|
syncLocalApp: 'Sync Local App',
|
||||||
localAppSyncToNodes: 'Include child nodes when syncing local apps',
|
|
||||||
memoryRequiredHelper: 'Current application memory requirement {0}',
|
memoryRequiredHelper: 'Current application memory requirement {0}',
|
||||||
gpuConfig: 'Enable GPU Support',
|
gpuConfig: 'Enable GPU Support',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -776,15 +776,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'Introduzca la IPv4 o el nombre de host usado en el navegador, sin protocolo, puerto ni ruta. HTTPS usa el certificado local de la aplicación.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Usuario: letras, números, punto, guion bajo o guion. Contraseña: al menos 12 caracteres, sin espacios al inicio o al final ni saltos de línea.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Contraseña: al menos 8 caracteres con letras y números; evite contraseñas comunes. Si se rechaza al iniciar, consulte las instrucciones en los registros de la aplicación.',
|
|
||||||
initialCredentials: 'Credenciales iniciales; si las cambió en la aplicación, use la contraseña actual.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'La instalación existente no tiene una dirección o puerto HTTPS compatible. Revise la configuración de la aplicación.',
|
|
||||||
agent: 'Agente',
|
agent: 'Agente',
|
||||||
account: 'Cuenta de modelo',
|
account: 'Cuenta de modelo',
|
||||||
batchOperation: 'Operación por lotes',
|
batchOperation: 'Operación por lotes',
|
||||||
@@ -3768,7 +3759,6 @@ const message = {
|
|||||||
link: 'Enlace',
|
link: 'Enlace',
|
||||||
showCurrentArch: 'Arquitectura',
|
showCurrentArch: 'Arquitectura',
|
||||||
syncLocalApp: 'Sincronizar app local',
|
syncLocalApp: 'Sincronizar app local',
|
||||||
localAppSyncToNodes: 'Incluir nodos secundarios al sincronizar apps locales',
|
|
||||||
memoryRequiredHelper: 'La app requiere {0} de memoria',
|
memoryRequiredHelper: 'La app requiere {0} de memoria',
|
||||||
gpuConfig: 'Habilitar soporte GPU',
|
gpuConfig: 'Habilitar soporte GPU',
|
||||||
gpuConfigHelper: 'Asegúrese de tener GPU NVIDIA, drivers y NVIDIA Docker Container Toolkit instalados',
|
gpuConfigHelper: 'Asegúrese de tener GPU NVIDIA, drivers y NVIDIA Docker Container Toolkit instalados',
|
||||||
|
|||||||
@@ -768,14 +768,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'نشانی IPv4 یا نام میزبان مورد استفاده در مرورگر را بدون پروتکل، درگاه یا مسیر وارد کنید. HTTPS از گواهی محلی برنامه استفاده میکند.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'نام کاربری: حروف لاتین، اعداد، نقطه، زیرخط یا خط تیره. گذرواژه: حداقل ۱۲ نویسه، بدون فاصله در ابتدا و انتها یا شکست خط.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'گذرواژه: حداقل ۸ نویسه شامل حروف و اعداد؛ از گذرواژههای رایج پرهیز کنید. اگر رد شد، راهنمای تنظیمات را در گزارشهای برنامه ببینید.',
|
|
||||||
initialCredentials: 'اطلاعات ورود اولیه؛ اگر در برنامه تغییر کرده، از گذرواژه فعلی استفاده کنید.',
|
|
||||||
nativeAgentAccessUnavailable: 'نصب موجود نشانی یا درگاه HTTPS سازگار ندارد. تنظیمات برنامه را بررسی کنید.',
|
|
||||||
agent: 'عامل',
|
agent: 'عامل',
|
||||||
account: 'حساب مدل',
|
account: 'حساب مدل',
|
||||||
batchOperation: 'عملیات دستهای',
|
batchOperation: 'عملیات دستهای',
|
||||||
@@ -3666,7 +3658,6 @@ const message = {
|
|||||||
link: 'لینک',
|
link: 'لینک',
|
||||||
showCurrentArch: 'معماری',
|
showCurrentArch: 'معماری',
|
||||||
syncLocalApp: 'همگامسازی برنامه محلی',
|
syncLocalApp: 'همگامسازی برنامه محلی',
|
||||||
localAppSyncToNodes: 'همگامسازی گرههای فرزند هنگام همگامسازی برنامههای محلی',
|
|
||||||
memoryRequiredHelper: 'نیاز حافظه برنامه فعلی {0}',
|
memoryRequiredHelper: 'نیاز حافظه برنامه فعلی {0}',
|
||||||
gpuConfig: 'فعالسازی پشتیبانی GPU',
|
gpuConfig: 'فعالسازی پشتیبانی GPU',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -772,15 +772,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'ブラウザーで使用する IPv4 アドレスまたはホスト名を、スキーム・ポート・パスなしで入力してください。HTTPS はアプリのローカル証明書を使用します。',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'ユーザー名には英数字、ドット、アンダースコア、ハイフンを使用できます。パスワードは12文字以上で、前後の空白や改行を含めないでください。',
|
|
||||||
octopAuthHelper:
|
|
||||||
'パスワードは英字と数字を含む8文字以上とし、よく使われるパスワードを避けてください。初期化時に拒否された場合は、アプリのログで設定手順を確認してください。',
|
|
||||||
initialCredentials: '初期ログイン情報です。アプリ内で変更した場合は現在のパスワードを使用してください。',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'既存のインストールに対応する HTTPS アクセス先またはポートがありません。アプリの設定を確認してください。',
|
|
||||||
agent: 'エージェント',
|
agent: 'エージェント',
|
||||||
account: 'モデルアカウント',
|
account: 'モデルアカウント',
|
||||||
batchOperation: '一括操作',
|
batchOperation: '一括操作',
|
||||||
@@ -3687,7 +3678,6 @@ const message = {
|
|||||||
link: 'リンク',
|
link: 'リンク',
|
||||||
showCurrentArch: '現在のサーバーアーキテクチャのアプリケーション',
|
showCurrentArch: '現在のサーバーアーキテクチャのアプリケーション',
|
||||||
syncLocalApp: 'ローカルアプリの同期',
|
syncLocalApp: 'ローカルアプリの同期',
|
||||||
localAppSyncToNodes: 'ローカルアプリ同期時に子ノードも同期',
|
|
||||||
memoryRequiredHelper: '現在のアプリケーションは {0} メモリが必要です',
|
memoryRequiredHelper: '現在のアプリケーションは {0} メモリが必要です',
|
||||||
gpuConfig: 'GPUサポートを有効化',
|
gpuConfig: 'GPUサポートを有効化',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -763,15 +763,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'브라우저에서 사용하는 IPv4 주소 또는 호스트 이름을 입력하세요. 프로토콜, 포트, 경로는 제외하세요. HTTPS는 앱의 로컬 인증서를 사용합니다.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'사용자 이름은 영문, 숫자, 점, 밑줄, 하이픈만 지원합니다. 비밀번호는 12자 이상이며 앞뒤 공백이나 줄바꿈을 포함할 수 없습니다.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'비밀번호는 문자와 숫자를 포함한 8자 이상이어야 하며 흔한 비밀번호는 피하세요. 초기화 시 거부되면 앱 로그의 설정 안내를 확인하세요.',
|
|
||||||
initialCredentials: '초기 로그인 정보입니다. 앱에서 변경했다면 현재 비밀번호를 사용하세요.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'기존 설치에 호환되는 HTTPS 접속 주소 또는 포트가 없습니다. 앱 설정을 확인하세요.',
|
|
||||||
agent: '에이전트',
|
agent: '에이전트',
|
||||||
account: '모델 계정',
|
account: '모델 계정',
|
||||||
batchOperation: '일괄 작업',
|
batchOperation: '일괄 작업',
|
||||||
@@ -3622,7 +3613,6 @@ const message = {
|
|||||||
link: '링크',
|
link: '링크',
|
||||||
showCurrentArch: '현재 서버 아키텍처 애플리케이션',
|
showCurrentArch: '현재 서버 아키텍처 애플리케이션',
|
||||||
syncLocalApp: '로컬 애플리케이션 동기화',
|
syncLocalApp: '로컬 애플리케이션 동기화',
|
||||||
localAppSyncToNodes: '로컬 앱 동기화 시 하위 노드도 동기화',
|
|
||||||
memoryRequiredHelper: '현재 애플리케이션은 {0} 메모리가 필요합니다',
|
memoryRequiredHelper: '현재 애플리케이션은 {0} 메모리가 필요합니다',
|
||||||
gpuConfig: 'GPU 지원 활성화',
|
gpuConfig: 'GPU 지원 활성화',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -761,15 +761,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'ປ້ອນທີ່ຢູ່ IPv4 ຫຼືຊື່ໂຮສທີ່ໃຊ້ໃນບຣາວເຊີ ໂດຍບໍ່ມີໂປຣໂຕຄອນ, ພອດ ຫຼືເສັ້ນທາງ. HTTPS ໃຊ້ໃບຢັ້ງຢືນພາຍໃນຂອງແອັບ.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'ຊື່ຜູ້ໃຊ້: ຕົວອັກສອນອັງກິດ, ຕົວເລກ, ຈຸດ, ຂີດລຸ່ມ ຫຼືຂີດກາງ. ລະຫັດຜ່ານ: ຢ່າງໜ້ອຍ 12 ຕົວ ໂດຍບໍ່ມີຊ່ອງຫວ່າງຢູ່ຫົວ/ທ້າຍ ຫຼືຂຶ້ນແຖວໃໝ່.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'ລະຫັດຜ່ານ: ຢ່າງໜ້ອຍ 8 ຕົວ ປະກອບດ້ວຍຕົວອັກສອນແລະຕົວເລກ; ຫຼີກລ້ຽງລະຫັດທີ່ໃຊ້ທົ່ວໄປ. ຖ້າຖືກປະຕິເສດ ໃຫ້ເບິ່ງຄຳແນະນຳໃນບັນທຶກຂອງແອັບ.',
|
|
||||||
initialCredentials: 'ຂໍ້ມູນເຂົ້າລະບົບເບື້ອງຕົ້ນ; ຖ້າປ່ຽນໃນແອັບແລ້ວ ໃຫ້ໃຊ້ລະຫັດປັດຈຸບັນ.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'ການຕິດຕັ້ງທີ່ມີຢູ່ບໍ່ມີທີ່ຢູ່ ຫຼືພອດ HTTPS ທີ່ເຂົ້າກັນໄດ້. ກະລຸນາກວດສອບການຕັ້ງຄ່າແອັບ.',
|
|
||||||
agent: 'ຕົວແທນ (Agent)',
|
agent: 'ຕົວແທນ (Agent)',
|
||||||
account: 'ບັນຊີໂມເດວ',
|
account: 'ບັນຊີໂມເດວ',
|
||||||
batchOperation: 'ດຳເນີນການແບບກຸ່ມ',
|
batchOperation: 'ດຳເນີນການແບບກຸ່ມ',
|
||||||
@@ -3599,7 +3590,6 @@ const message = {
|
|||||||
link: 'ລິ້ງ',
|
link: 'ລິ້ງ',
|
||||||
showCurrentArch: 'ສະຖາປັດຕະຍະກຳ',
|
showCurrentArch: 'ສະຖາປັດຕະຍະກຳ',
|
||||||
syncLocalApp: 'ຊິງຄ໌ແອັບໃນເຄື່ອງ',
|
syncLocalApp: 'ຊິງຄ໌ແອັບໃນເຄື່ອງ',
|
||||||
localAppSyncToNodes: 'ຮວມໂນດລູກເມື່ອຊິງຄ໌ແອັບໃນເຄື່ອງ',
|
|
||||||
memoryRequiredHelper: 'ຄວາມຕ້ອງການໜ່ວຍຄວາມຈຳຂອງແອັບປະຈຸບັນ {0}',
|
memoryRequiredHelper: 'ຄວາມຕ້ອງການໜ່ວຍຄວາມຈຳຂອງແອັບປະຈຸບັນ {0}',
|
||||||
gpuConfig: 'ເປີດໃຊ້ການຮອງຮັບ GPU',
|
gpuConfig: 'ເປີດໃຊ້ການຮອງຮັບ GPU',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -781,15 +781,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'Masukkan alamat IPv4 atau nama hos yang digunakan dalam pelayar, tanpa protokol, port atau laluan. HTTPS menggunakan sijil setempat aplikasi.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Nama pengguna: huruf, nombor, titik, garis bawah atau sempang. Kata laluan: sekurang-kurangnya 12 aksara, tanpa ruang di awal/akhir atau baris baharu.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Kata laluan: sekurang-kurangnya 8 aksara dengan huruf dan nombor; elakkan kata laluan lazim. Jika ditolak, semak arahan persediaan dalam log aplikasi.',
|
|
||||||
initialCredentials: 'Maklumat log masuk awal; gunakan kata laluan semasa jika telah diubah dalam aplikasi.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'Pemasangan sedia ada tiada alamat atau port HTTPS yang serasi. Semak tetapan aplikasi.',
|
|
||||||
agent: 'Agen',
|
agent: 'Agen',
|
||||||
account: 'Akaun model',
|
account: 'Akaun model',
|
||||||
batchOperation: 'Operasi pukal',
|
batchOperation: 'Operasi pukal',
|
||||||
@@ -3759,7 +3750,6 @@ const message = {
|
|||||||
link: 'Pautan',
|
link: 'Pautan',
|
||||||
showCurrentArch: 'Aplikasi seni bina pelayan semasa',
|
showCurrentArch: 'Aplikasi seni bina pelayan semasa',
|
||||||
syncLocalApp: 'Segerakkan Aplikasi Tempatan',
|
syncLocalApp: 'Segerakkan Aplikasi Tempatan',
|
||||||
localAppSyncToNodes: 'Sertakan nod anak semasa menyegerakkan aplikasi tempatan',
|
|
||||||
memoryRequiredHelper: 'Aplikasi semasa memerlukan {0} memori',
|
memoryRequiredHelper: 'Aplikasi semasa memerlukan {0} memori',
|
||||||
gpuConfig: 'Aktifkan Sokongan GPU',
|
gpuConfig: 'Aktifkan Sokongan GPU',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -774,15 +774,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'Informe o IPv4 ou nome do host usado no navegador, sem protocolo, porta ou caminho. O HTTPS usa o certificado local do aplicativo.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Usuário: letras, números, ponto, sublinhado ou hífen. Senha: no mínimo 12 caracteres, sem espaços nas extremidades ou quebras de linha.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Senha: no mínimo 8 caracteres com letras e números; evite senhas comuns. Se rejeitada na inicialização, consulte as instruções nos logs do aplicativo.',
|
|
||||||
initialCredentials: 'Credenciais iniciais; se alteradas no aplicativo, use a senha atual.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'A instalação existente não tem endereço ou porta HTTPS compatível. Verifique as configurações do aplicativo.',
|
|
||||||
agent: 'Agente',
|
agent: 'Agente',
|
||||||
account: 'Conta de modelo',
|
account: 'Conta de modelo',
|
||||||
batchOperation: 'Operação em lote',
|
batchOperation: 'Operação em lote',
|
||||||
@@ -3775,7 +3766,6 @@ const message = {
|
|||||||
link: 'Link',
|
link: 'Link',
|
||||||
showCurrentArch: 'Aplicações da arquitetura atual do servidor',
|
showCurrentArch: 'Aplicações da arquitetura atual do servidor',
|
||||||
syncLocalApp: 'Sincronizar Aplicativo Local',
|
syncLocalApp: 'Sincronizar Aplicativo Local',
|
||||||
localAppSyncToNodes: 'Incluir nós filhos ao sincronizar apps locais',
|
|
||||||
memoryRequiredHelper: 'O aplicativo atual requer {0} de memória',
|
memoryRequiredHelper: 'O aplicativo atual requer {0} de memória',
|
||||||
gpuConfig: 'Ativar Suporte a GPU',
|
gpuConfig: 'Ativar Suporte a GPU',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -772,15 +772,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'Введите IPv4-адрес или имя хоста, используемое в браузере, без протокола, порта и пути. HTTPS использует локальный сертификат приложения.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Имя пользователя: латинские буквы, цифры, точка, подчёркивание или дефис. Пароль: минимум 12 символов, без пробелов по краям и переводов строк.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Пароль: минимум 8 символов с буквами и цифрами; избегайте распространённых паролей. Если пароль отклонён, инструкции доступны в журнале приложения.',
|
|
||||||
initialCredentials: 'Начальные учётные данные; если пароль изменён в приложении, используйте текущий.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'У существующей установки нет совместимого HTTPS-адреса или порта. Проверьте настройки приложения.',
|
|
||||||
agent: 'Агент',
|
agent: 'Агент',
|
||||||
account: 'Аккаунт модели',
|
account: 'Аккаунт модели',
|
||||||
batchOperation: 'Пакетная операция',
|
batchOperation: 'Пакетная операция',
|
||||||
@@ -3744,7 +3735,6 @@ const message = {
|
|||||||
link: 'Ссылка',
|
link: 'Ссылка',
|
||||||
showCurrentArch: 'Приложения для текущей архитектуры сервера',
|
showCurrentArch: 'Приложения для текущей архитектуры сервера',
|
||||||
syncLocalApp: 'Синхронизировать локальное приложение',
|
syncLocalApp: 'Синхронизировать локальное приложение',
|
||||||
localAppSyncToNodes: 'Включать дочерние узлы при синхронизации локальных приложений',
|
|
||||||
memoryRequiredHelper: 'Текущее приложение требует {0} памяти',
|
memoryRequiredHelper: 'Текущее приложение требует {0} памяти',
|
||||||
gpuConfig: 'Включить поддержку GPU',
|
gpuConfig: 'Включить поддержку GPU',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -777,15 +777,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'Tarayıcıda kullanılan IPv4 adresini veya ana bilgisayar adını protokol, bağlantı noktası veya yol olmadan girin. HTTPS, uygulamanın yerel sertifikasını kullanır.',
|
|
||||||
deepSeekAuthHelper:
|
|
||||||
'Kullanıcı adı: harf, rakam, nokta, alt çizgi veya tire. Parola: en az 12 karakter; başta/sonda boşluk veya satır sonu içeremez.',
|
|
||||||
octopAuthHelper:
|
|
||||||
'Parola: harf ve rakam içeren en az 8 karakter; yaygın parolalardan kaçının. Reddedilirse kurulum yönergeleri için uygulama günlüklerini kontrol edin.',
|
|
||||||
initialCredentials: 'İlk giriş bilgileri; uygulamada değiştirildiyse güncel parolayı kullanın.',
|
|
||||||
nativeAgentAccessUnavailable:
|
|
||||||
'Mevcut kurulumda uyumlu HTTPS erişim adresi veya bağlantı noktası yok. Uygulama ayarlarını kontrol edin.',
|
|
||||||
agent: 'Ajan',
|
agent: 'Ajan',
|
||||||
account: 'Model hesabı',
|
account: 'Model hesabı',
|
||||||
batchOperation: 'Toplu işlem',
|
batchOperation: 'Toplu işlem',
|
||||||
@@ -3760,7 +3751,6 @@ const message = {
|
|||||||
link: 'Bağlantı',
|
link: 'Bağlantı',
|
||||||
showCurrentArch: 'Mimari',
|
showCurrentArch: 'Mimari',
|
||||||
syncLocalApp: 'Yerel Uygulamayı Senkronize Et',
|
syncLocalApp: 'Yerel Uygulamayı Senkronize Et',
|
||||||
localAppSyncToNodes: 'Yerel uygulama eşitlemesine alt düğümleri dahil et',
|
|
||||||
memoryRequiredHelper: 'Geçerli uygulama bellek gereksinimi {0}',
|
memoryRequiredHelper: 'Geçerli uygulama bellek gereksinimi {0}',
|
||||||
gpuConfig: 'GPU Desteğini Etkinleştir',
|
gpuConfig: 'GPU Desteğini Etkinleştir',
|
||||||
gpuConfigHelper:
|
gpuConfigHelper:
|
||||||
|
|||||||
@@ -739,13 +739,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'填寫瀏覽器實際存取的 IPv4 位址或主機名稱,不含協定、連接埠或路徑。HTTPS 使用應用程式的本機憑證。',
|
|
||||||
deepSeekAuthHelper: '使用者名稱僅支援字母、數字、點、底線及連字號;密碼至少 12 位,不含前後空格或換行。',
|
|
||||||
octopAuthHelper:
|
|
||||||
'密碼至少 8 位且包含字母及數字,避免常見弱密碼。若初始化拒絕該密碼,請查看應用程式日誌中的設定說明。',
|
|
||||||
initialCredentials: '初始登入資訊;若已在應用程式內修改,以目前密碼為準。',
|
|
||||||
nativeAgentAccessUnavailable: '現有安裝缺少相容的 HTTPS 存取位址或連接埠,請檢查應用程式設定。',
|
|
||||||
agent: 'Agent',
|
agent: 'Agent',
|
||||||
account: '模型帳號',
|
account: '模型帳號',
|
||||||
batchOperation: '批次操作',
|
batchOperation: '批次操作',
|
||||||
@@ -3458,7 +3451,6 @@ const message = {
|
|||||||
link: '連結',
|
link: '連結',
|
||||||
showCurrentArch: '目前伺服器架構應用',
|
showCurrentArch: '目前伺服器架構應用',
|
||||||
syncLocalApp: '同步本機應用',
|
syncLocalApp: '同步本機應用',
|
||||||
localAppSyncToNodes: '同步本機應用時聯動子節點',
|
|
||||||
memoryRequiredHelper: '目前應用記憶體需求 {0}',
|
memoryRequiredHelper: '目前應用記憶體需求 {0}',
|
||||||
gpuConfig: '開啟 GPU 支援',
|
gpuConfig: '開啟 GPU 支援',
|
||||||
gpuConfigHelper: '請確保機器有 NVIDIA GPU 並且安裝 NVIDIA 驅動 和 NVIDIA docker Container Toolkit',
|
gpuConfigHelper: '請確保機器有 NVIDIA GPU 並且安裝 NVIDIA 驅動 和 NVIDIA docker Container Toolkit',
|
||||||
|
|||||||
@@ -756,13 +756,6 @@ const message = {
|
|||||||
},
|
},
|
||||||
aiTools: {
|
aiTools: {
|
||||||
agents: {
|
agents: {
|
||||||
deepSeekAccessHostHelper:
|
|
||||||
'填写浏览器实际访问的 IPv4 地址或主机名,不含协议、端口或路径。HTTPS 使用应用的本地证书。',
|
|
||||||
deepSeekAuthHelper: '用户名仅支持字母、数字、点、下划线和连字符;密码至少 12 位,不含首尾空格或换行。',
|
|
||||||
octopAuthHelper:
|
|
||||||
'密码至少 8 位且包含字母和数字,避免常见弱密码。若初始化拒绝该密码,请查看应用日志中的设置说明。',
|
|
||||||
initialCredentials: '初始登录信息;若已在应用内修改,以当前密码为准。',
|
|
||||||
nativeAgentAccessUnavailable: '现有安装缺少兼容的 HTTPS 访问地址或端口,请检查应用设置。',
|
|
||||||
agent: '智能体',
|
agent: '智能体',
|
||||||
account: '模型账号',
|
account: '模型账号',
|
||||||
batchOperation: '批量操作',
|
batchOperation: '批量操作',
|
||||||
@@ -3499,7 +3492,6 @@ const message = {
|
|||||||
link: '链接',
|
link: '链接',
|
||||||
showCurrentArch: '本服务器架构应用',
|
showCurrentArch: '本服务器架构应用',
|
||||||
syncLocalApp: '同步本地应用',
|
syncLocalApp: '同步本地应用',
|
||||||
localAppSyncToNodes: '同步本地应用时联动子节点',
|
|
||||||
memoryRequiredHelper: '当前应用内存需求 {0}',
|
memoryRequiredHelper: '当前应用内存需求 {0}',
|
||||||
gpuConfig: '开启 GPU 支持',
|
gpuConfig: '开启 GPU 支持',
|
||||||
gpuConfigHelper: '请确保机器有 NVIDIA GPU 并且安装 NVIDIA 驱动 和 NVIDIA docker Container Toolkit',
|
gpuConfigHelper: '请确保机器有 NVIDIA GPU 并且安装 NVIDIA 驱动 和 NVIDIA docker Container Toolkit',
|
||||||
|
|||||||
@@ -1,52 +1,10 @@
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { compareVersion } from '@/utils/version';
|
import { compareVersion } from '@/utils/version';
|
||||||
import type { AI } from '@/api/interface/ai';
|
|
||||||
|
|
||||||
const openclawHTTPSVersion = '2026.3.13';
|
const openclawHTTPSVersion = '2026.3.13';
|
||||||
const openclawHTTPVersion = '2026.3.23';
|
const openclawHTTPVersion = '2026.3.23';
|
||||||
const openclawDefaultAccessHost = '127.0.0.1';
|
const openclawDefaultAccessHost = '127.0.0.1';
|
||||||
|
|
||||||
export const isAgentType = (value: unknown): value is AI.AgentType =>
|
|
||||||
typeof value === 'string' && ['openclaw', 'copaw', 'hermes-agent', 'deepseek-harness', 'octop'].includes(value);
|
|
||||||
|
|
||||||
export const isDeepSeekAccessHost = (value: string): boolean => {
|
|
||||||
if (/^[0-9.]+$/.test(value)) {
|
|
||||||
return (
|
|
||||||
value.split('.').length === 4 &&
|
|
||||||
value.split('.').every((part) => /^(0|[1-9]\d{0,2})$/.test(part) && +part <= 255)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
value.length > 0 &&
|
|
||||||
value.length <= 253 &&
|
|
||||||
value === value.trim() &&
|
|
||||||
value.split('.').every((part) => /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/.test(part))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isOctopPassword = (value: string): boolean =>
|
|
||||||
[...value].length >= 8 &&
|
|
||||||
value === value.trim() &&
|
|
||||||
!/[\r\n\0]/.test(value) &&
|
|
||||||
/\p{L}/u.test(value) &&
|
|
||||||
/\p{Nd}/u.test(value) &&
|
|
||||||
![
|
|
||||||
'password',
|
|
||||||
'password1',
|
|
||||||
'password12',
|
|
||||||
'password123',
|
|
||||||
'12345678',
|
|
||||||
'123456789',
|
|
||||||
'qwerty123',
|
|
||||||
'admin123',
|
|
||||||
'welcome1',
|
|
||||||
'letmein1',
|
|
||||||
'changeme1',
|
|
||||||
'octop123',
|
|
||||||
'abc12345',
|
|
||||||
'iloveyou1',
|
|
||||||
].includes(value.toLowerCase());
|
|
||||||
|
|
||||||
export const getAgentProviderDisplayName = (provider: string, displayName?: string): string => {
|
export const getAgentProviderDisplayName = (provider: string, displayName?: string): string => {
|
||||||
if (provider === 'custom' || displayName === 'Custom') {
|
if (provider === 'custom' || displayName === 'Custom') {
|
||||||
return i18n.global.t('container.custom');
|
return i18n.global.t('container.custom');
|
||||||
@@ -69,7 +27,7 @@ export const supportsAgentToken = (agentType: string): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const supportsAgentDashboardAuth = (agentType: string): boolean => {
|
export const supportsAgentDashboardAuth = (agentType: string): boolean => {
|
||||||
return ['hermes-agent', 'copaw', 'deepseek-harness', 'octop'].includes(agentType);
|
return agentType === 'hermes-agent' || agentType === 'copaw';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isOpenclawHTTPSWindowVersion = (version: string): boolean => {
|
export const isOpenclawHTTPSWindowVersion = (version: string): boolean => {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
<el-option label="OpenClaw" value="openclaw" />
|
<el-option label="OpenClaw" value="openclaw" />
|
||||||
<el-option label="Hermes Agent" value="hermes-agent" />
|
<el-option label="Hermes Agent" value="hermes-agent" />
|
||||||
<el-option label="QwenPaw" value="copaw" />
|
<el-option label="QwenPaw" value="copaw" />
|
||||||
<el-option label="DeepSeek Harness" value="deepseek-harness" />
|
|
||||||
<el-option label="Octop" value="octop" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
@@ -21,19 +19,10 @@
|
|||||||
<el-select v-model="form.appVersion" filterable>
|
<el-select v-model="form.appVersion" filterable>
|
||||||
<el-option v-for="item in versions" :key="item" :label="item" :value="item" />
|
<el-option v-for="item in versions" :key="item" :label="item" :value="item" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<span v-if="versions.length === 0" class="input-help">{{ $t('commons.msg.noneData') }}</span>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('aiTools.agents.webuiPort')" prop="webUIPort">
|
<el-form-item :label="$t('aiTools.agents.webuiPort')" prop="webUIPort">
|
||||||
<el-input-number v-model="form.webUIPort" :min="1" :max="65535" />
|
<el-input-number v-model="form.webUIPort" :min="1" :max="65535" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
|
||||||
v-if="form.agentType === 'deepseek-harness'"
|
|
||||||
:label="$t('setting.systemIP')"
|
|
||||||
prop="accessHost"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.accessHost" />
|
|
||||||
<span class="input-help">{{ $t('aiTools.agents.deepSeekAccessHostHelper') }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<template v-if="supportsAgentDashboardAuth(form.agentType)">
|
<template v-if="supportsAgentDashboardAuth(form.agentType)">
|
||||||
<el-form-item :label="$t('commons.login.username')" prop="dashboardUsername">
|
<el-form-item :label="$t('commons.login.username')" prop="dashboardUsername">
|
||||||
<el-input v-model="form.dashboardUsername" />
|
<el-input v-model="form.dashboardUsername" />
|
||||||
@@ -46,12 +35,6 @@
|
|||||||
{{ $t('commons.button.random') }}
|
{{ $t('commons.button.random') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<span class="input-help" v-if="form.agentType === 'deepseek-harness'">
|
|
||||||
{{ $t('aiTools.agents.deepSeekAuthHelper') }}
|
|
||||||
</span>
|
|
||||||
<span class="input-help" v-if="form.agentType === 'octop'">
|
|
||||||
{{ $t('aiTools.agents.octopAuthHelper') }}
|
|
||||||
</span>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@@ -147,9 +130,6 @@ import { getRandomStr, newUUID } from '@/utils/id';
|
|||||||
import {
|
import {
|
||||||
buildDefaultAllowedOrigin,
|
buildDefaultAllowedOrigin,
|
||||||
getAgentProviderDisplayName,
|
getAgentProviderDisplayName,
|
||||||
isAgentType,
|
|
||||||
isDeepSeekAccessHost,
|
|
||||||
isOctopPassword,
|
|
||||||
parseAllowedOriginsInput,
|
parseAllowedOriginsInput,
|
||||||
supportsAgentDashboardAuth,
|
supportsAgentDashboardAuth,
|
||||||
validateAllowedOriginsInput,
|
validateAllowedOriginsInput,
|
||||||
@@ -158,7 +138,6 @@ import { App } from '@/api/interface/app';
|
|||||||
import AdvancedSetting from '@/components/advanced-setting/index.vue';
|
import AdvancedSetting from '@/components/advanced-setting/index.vue';
|
||||||
import AccountAddDialog from '@/views/ai/agents/model/add/index.vue';
|
import AccountAddDialog from '@/views/ai/agents/model/add/index.vue';
|
||||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||||
import i18n from '@/lang';
|
|
||||||
|
|
||||||
const emit = defineEmits(['search', 'task']);
|
const emit = defineEmits(['search', 'task']);
|
||||||
|
|
||||||
@@ -182,7 +161,6 @@ const form = reactive({
|
|||||||
agentType: 'openclaw' as AI.AgentType,
|
agentType: 'openclaw' as AI.AgentType,
|
||||||
appVersion: '',
|
appVersion: '',
|
||||||
webUIPort: 18789,
|
webUIPort: 18789,
|
||||||
accessHost: '',
|
|
||||||
allowedOrigins: '',
|
allowedOrigins: '',
|
||||||
provider: '',
|
provider: '',
|
||||||
accountId: undefined as unknown as number,
|
accountId: undefined as unknown as number,
|
||||||
@@ -207,10 +185,7 @@ const form = reactive({
|
|||||||
const showModelConfig = computed(() => form.agentType === 'openclaw' || form.agentType === 'hermes-agent');
|
const showModelConfig = computed(() => form.agentType === 'openclaw' || form.agentType === 'hermes-agent');
|
||||||
|
|
||||||
const generateDashboardPassword = () => {
|
const generateDashboardPassword = () => {
|
||||||
form.dashboardPassword =
|
form.dashboardPassword = getRandomStr(8);
|
||||||
form.agentType === 'deepseek-harness' || form.agentType === 'octop'
|
|
||||||
? `Aa2${Array.from(crypto.getRandomValues(new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, '0')).join('')}`
|
|
||||||
: getRandomStr(8);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ensureDashboardAuth = () => {
|
const ensureDashboardAuth = () => {
|
||||||
@@ -226,7 +201,7 @@ const ensureDashboardAuth = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setDefaultWebUIPort = () => {
|
const setDefaultWebUIPort = () => {
|
||||||
if (form.agentType === 'copaw' || form.agentType === 'octop') {
|
if (form.agentType === 'copaw') {
|
||||||
form.webUIPort = 8088;
|
form.webUIPort = 8088;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -234,10 +209,6 @@ const setDefaultWebUIPort = () => {
|
|||||||
form.webUIPort = 9119;
|
form.webUIPort = 9119;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (form.agentType === 'deepseek-harness') {
|
|
||||||
form.webUIPort = 10443;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
form.webUIPort = 18789;
|
form.webUIPort = 18789;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -246,19 +217,6 @@ const rules = reactive({
|
|||||||
agentType: [Rules.requiredSelect],
|
agentType: [Rules.requiredSelect],
|
||||||
appVersion: [Rules.requiredSelect],
|
appVersion: [Rules.requiredSelect],
|
||||||
webUIPort: [Rules.requiredInput],
|
webUIPort: [Rules.requiredInput],
|
||||||
accessHost: [
|
|
||||||
Rules.requiredInput,
|
|
||||||
{
|
|
||||||
validator: (_rule: any, value: string, callback: (error?: Error) => void) => {
|
|
||||||
callback(
|
|
||||||
isDeepSeekAccessHost(value)
|
|
||||||
? undefined
|
|
||||||
: new Error(i18n.global.t('aiTools.agents.deepSeekAccessHostHelper')),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
allowedOrigins: [
|
allowedOrigins: [
|
||||||
Rules.requiredInput,
|
Rules.requiredInput,
|
||||||
{
|
{
|
||||||
@@ -276,35 +234,8 @@ const rules = reactive({
|
|||||||
provider: [Rules.requiredSelect],
|
provider: [Rules.requiredSelect],
|
||||||
accountId: [Rules.requiredSelect],
|
accountId: [Rules.requiredSelect],
|
||||||
model: [Rules.requiredInput],
|
model: [Rules.requiredInput],
|
||||||
dashboardUsername: [
|
dashboardUsername: [Rules.requiredInput],
|
||||||
Rules.requiredInput,
|
dashboardPassword: [Rules.requiredInput],
|
||||||
{
|
|
||||||
validator: (_rule: any, value: string, callback: (error?: Error) => void) => {
|
|
||||||
callback(
|
|
||||||
form.agentType === 'deepseek-harness' && !/^[A-Za-z0-9._-]+$/.test(value)
|
|
||||||
? new Error(i18n.global.t('aiTools.agents.deepSeekAuthHelper'))
|
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dashboardPassword: [
|
|
||||||
Rules.requiredInput,
|
|
||||||
{
|
|
||||||
validator: (_rule: any, value: string, callback: (error?: Error) => void) => {
|
|
||||||
const invalidCharacters = value !== value.trim() || /[\r\n\0]/.test(value);
|
|
||||||
if (form.agentType === 'deepseek-harness' && (invalidCharacters || [...value].length < 12)) {
|
|
||||||
callback(new Error(i18n.global.t('aiTools.agents.deepSeekAuthHelper')));
|
|
||||||
} else if (form.agentType === 'octop' && !isOctopPassword(value)) {
|
|
||||||
callback(new Error(i18n.global.t('aiTools.agents.octopAuthHelper')));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
containerName: [Rules.containerName],
|
containerName: [Rules.containerName],
|
||||||
restartPolicy: [Rules.requiredSelect],
|
restartPolicy: [Rules.requiredSelect],
|
||||||
cpuQuota: [checkNumberRange(0, 99999)],
|
cpuQuota: [checkNumberRange(0, 99999)],
|
||||||
@@ -342,13 +273,7 @@ const loadSystemIP = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadVersions = async (appKey: AI.AgentType) => {
|
const loadVersions = async (appKey: AI.AgentType) => {
|
||||||
versions.value = [];
|
|
||||||
appInfo.value = undefined;
|
|
||||||
form.dockerCompose = '';
|
|
||||||
const res = await getAppByKey(appKey);
|
const res = await getAppByKey(appKey);
|
||||||
if (form.agentType !== appKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
appInfo.value = res.data;
|
appInfo.value = res.data;
|
||||||
versions.value = res.data.versions || [];
|
versions.value = res.data.versions || [];
|
||||||
if (!form.appVersion && versions.value.length > 0) {
|
if (!form.appVersion && versions.value.length > 0) {
|
||||||
@@ -362,10 +287,6 @@ const getDefaultAgentName = (agentType: AI.AgentType) => {
|
|||||||
return 'QwenPaw';
|
return 'QwenPaw';
|
||||||
case 'hermes-agent':
|
case 'hermes-agent':
|
||||||
return 'Hermes-Agent';
|
return 'Hermes-Agent';
|
||||||
case 'deepseek-harness':
|
|
||||||
return 'DeepSeek-Harness';
|
|
||||||
case 'octop':
|
|
||||||
return 'Octop';
|
|
||||||
default:
|
default:
|
||||||
return 'OpenClaw';
|
return 'OpenClaw';
|
||||||
}
|
}
|
||||||
@@ -481,9 +402,7 @@ const handleAgentTypeChange = async () => {
|
|||||||
form.name === 'OpenClaw' ||
|
form.name === 'OpenClaw' ||
|
||||||
form.name === 'CoPaw' ||
|
form.name === 'CoPaw' ||
|
||||||
form.name === 'QwenPaw' ||
|
form.name === 'QwenPaw' ||
|
||||||
form.name === 'Hermes-Agent' ||
|
form.name === 'Hermes-Agent'
|
||||||
form.name === 'DeepSeek-Harness' ||
|
|
||||||
form.name === 'Octop'
|
|
||||||
) {
|
) {
|
||||||
form.name = getDefaultAgentName(form.agentType);
|
form.name = getDefaultAgentName(form.agentType);
|
||||||
}
|
}
|
||||||
@@ -494,10 +413,6 @@ const handleAgentTypeChange = async () => {
|
|||||||
form.accountId = undefined as unknown as number;
|
form.accountId = undefined as unknown as number;
|
||||||
form.baseURL = '';
|
form.baseURL = '';
|
||||||
ensureDashboardAuth();
|
ensureDashboardAuth();
|
||||||
if (form.agentType === 'deepseek-harness' && !form.accessHost) {
|
|
||||||
await loadSystemIP();
|
|
||||||
form.accessHost = isDeepSeekAccessHost(systemIP.value) ? systemIP.value : '';
|
|
||||||
}
|
|
||||||
if (form.agentType === 'openclaw') {
|
if (form.agentType === 'openclaw') {
|
||||||
await loadSystemIP();
|
await loadSystemIP();
|
||||||
allowedOriginsAutoFilled.value = true;
|
allowedOriginsAutoFilled.value = true;
|
||||||
@@ -570,7 +485,6 @@ const submit = async () => {
|
|||||||
webUIPort: form.webUIPort,
|
webUIPort: form.webUIPort,
|
||||||
allowedOrigins: form.agentType === 'openclaw' ? parseAllowedOriginsInput(form.allowedOrigins) : undefined,
|
allowedOrigins: form.agentType === 'openclaw' ? parseAllowedOriginsInput(form.allowedOrigins) : undefined,
|
||||||
agentType: form.agentType,
|
agentType: form.agentType,
|
||||||
accessHost: form.agentType === 'deepseek-harness' ? form.accessHost : undefined,
|
|
||||||
model: showModelConfig.value ? form.model : undefined,
|
model: showModelConfig.value ? form.model : undefined,
|
||||||
accountId: showModelConfig.value ? form.accountId : undefined,
|
accountId: showModelConfig.value ? form.accountId : undefined,
|
||||||
token: form.agentType === 'openclaw' ? form.token : undefined,
|
token: form.agentType === 'openclaw' ? form.token : undefined,
|
||||||
@@ -610,19 +524,45 @@ const handleClose = () => {
|
|||||||
form.dashboardPassword = '';
|
form.dashboardPassword = '';
|
||||||
form.remark = '';
|
form.remark = '';
|
||||||
form.allowedOrigins = '';
|
form.allowedOrigins = '';
|
||||||
form.accessHost = '';
|
|
||||||
form.dockerCompose = '';
|
form.dockerCompose = '';
|
||||||
lastAutoAllowedOrigins.value = '';
|
lastAutoAllowedOrigins.value = '';
|
||||||
allowedOriginsAutoFilled.value = true;
|
allowedOriginsAutoFilled.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const openDrawer = async (agentType?: AI.AgentType) => {
|
const openDrawer = async (agentType?: AI.AgentType) => {
|
||||||
const targetType = isAgentType(agentType) ? agentType : 'openclaw';
|
const targetType =
|
||||||
|
agentType === 'copaw' || agentType === 'hermes-agent' || agentType === 'openclaw' ? agentType : 'openclaw';
|
||||||
form.name = getDefaultAgentName(targetType);
|
form.name = getDefaultAgentName(targetType);
|
||||||
open.value = true;
|
open.value = true;
|
||||||
form.agentType = targetType;
|
form.agentType = targetType;
|
||||||
|
setDefaultWebUIPort();
|
||||||
form.token = getRandomStr(32).toLowerCase();
|
form.token = getRandomStr(32).toLowerCase();
|
||||||
await handleAgentTypeChange();
|
ensureDashboardAuth();
|
||||||
|
if (form.agentType === 'copaw') {
|
||||||
|
form.allowedOrigins = '';
|
||||||
|
lastAutoAllowedOrigins.value = '';
|
||||||
|
allowedOriginsAutoFilled.value = true;
|
||||||
|
await loadVersions('copaw');
|
||||||
|
providerOptions.value = [];
|
||||||
|
providerModels.value = {};
|
||||||
|
accountOptions.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (form.agentType === 'hermes-agent') {
|
||||||
|
form.allowedOrigins = '';
|
||||||
|
lastAutoAllowedOrigins.value = '';
|
||||||
|
allowedOriginsAutoFilled.value = true;
|
||||||
|
await loadVersions('hermes-agent');
|
||||||
|
await loadProviders();
|
||||||
|
await loadAccounts();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await loadSystemIP();
|
||||||
|
allowedOriginsAutoFilled.value = true;
|
||||||
|
syncAllowedOriginsWithDefault(true);
|
||||||
|
await loadVersions('openclaw');
|
||||||
|
await loadProviders();
|
||||||
|
await loadAccounts();
|
||||||
};
|
};
|
||||||
|
|
||||||
const openAccountCreate = () => {
|
const openAccountCreate = () => {
|
||||||
|
|||||||
@@ -178,9 +178,6 @@
|
|||||||
alignment="start"
|
alignment="start"
|
||||||
:size="2"
|
:size="2"
|
||||||
>
|
>
|
||||||
<el-text v-if="row.agentType === 'octop'" type="info" size="small">
|
|
||||||
{{ $t('aiTools.agents.initialCredentials') }}
|
|
||||||
</el-text>
|
|
||||||
<span>{{ row.dashboardUsername || '-' }}</span>
|
<span>{{ row.dashboardUsername || '-' }}</span>
|
||||||
<el-space alignment="center">
|
<el-space alignment="center">
|
||||||
<span>{{ row.dashboardPassword ? '********' : '-' }}</span>
|
<span>{{ row.dashboardPassword ? '********' : '-' }}</span>
|
||||||
@@ -286,7 +283,7 @@ import { Website } from '@/api/interface/website';
|
|||||||
import { SearchWithPage } from '@/api/interface';
|
import { SearchWithPage } from '@/api/interface';
|
||||||
import { dateFormat } from '@/utils/date';
|
import { dateFormat } from '@/utils/date';
|
||||||
import { newUUID } from '@/utils/id';
|
import { newUUID } from '@/utils/id';
|
||||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
import AddDialog from '@/views/ai/agents/agent/add/index.vue';
|
import AddDialog from '@/views/ai/agents/agent/add/index.vue';
|
||||||
import DeleteDialog from '@/views/ai/agents/agent/delete/index.vue';
|
import DeleteDialog from '@/views/ai/agents/agent/delete/index.vue';
|
||||||
@@ -305,8 +302,6 @@ import DockerStatus from '@/views/container/docker-status/index.vue';
|
|||||||
import {
|
import {
|
||||||
getAgentProviderDisplayName,
|
getAgentProviderDisplayName,
|
||||||
getOpenclawAccessScheme,
|
getOpenclawAccessScheme,
|
||||||
isAgentType,
|
|
||||||
isDeepSeekAccessHost,
|
|
||||||
supportsAgentModelConfig,
|
supportsAgentModelConfig,
|
||||||
supportsAgentToken,
|
supportsAgentToken,
|
||||||
supportsAgentDashboardAuth,
|
supportsAgentDashboardAuth,
|
||||||
@@ -317,8 +312,6 @@ import NoApp from '@/views/app-store/apps/no-app/index.vue';
|
|||||||
import openclawIcon from '@/assets/images/ai-agent-openclaw.svg';
|
import openclawIcon from '@/assets/images/ai-agent-openclaw.svg';
|
||||||
import copawIcon from '@/assets/images/ai-agent-copaw.svg';
|
import copawIcon from '@/assets/images/ai-agent-copaw.svg';
|
||||||
import hermesIcon from '@/assets/images/ai-agent-hermes-agent.svg';
|
import hermesIcon from '@/assets/images/ai-agent-hermes-agent.svg';
|
||||||
import deepSeekIcon from '@/assets/images/ai-agent-deepseek-harness.png';
|
|
||||||
import octopIcon from '@/assets/images/ai-agent-octop.png';
|
|
||||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||||
import { loadOptionalComponent } from '@/extensions/optional';
|
import { loadOptionalComponent } from '@/extensions/optional';
|
||||||
import { useOperateNodeContext } from '@/composables/useOperateNodeContext';
|
import { useOperateNodeContext } from '@/composables/useOperateNodeContext';
|
||||||
@@ -365,7 +358,8 @@ const buttons = [
|
|||||||
{
|
{
|
||||||
label: i18n.global.t('menu.config'),
|
label: i18n.global.t('menu.config'),
|
||||||
click: (row: AI.AgentItem) => openConfig(row),
|
click: (row: AI.AgentItem) => openConfig(row),
|
||||||
show: (row: AI.AgentItem) => supportsAgentModelConfig(row.agentType) || row.agentType === 'copaw',
|
show: (row: AI.AgentItem) =>
|
||||||
|
supportsAgentModelConfig(row.agentType) || supportsAgentDashboardAuth(row.agentType),
|
||||||
disabled: (row: AI.AgentItem) => row.status !== 'Running',
|
disabled: (row: AI.AgentItem) => row.status !== 'Running',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -447,10 +441,6 @@ const getAgentTypeLabel = (agentType: AI.AgentType) => {
|
|||||||
return 'QwenPaw';
|
return 'QwenPaw';
|
||||||
case 'hermes-agent':
|
case 'hermes-agent':
|
||||||
return 'Hermes Agent';
|
return 'Hermes Agent';
|
||||||
case 'deepseek-harness':
|
|
||||||
return 'DeepSeek Harness';
|
|
||||||
case 'octop':
|
|
||||||
return 'Octop';
|
|
||||||
default:
|
default:
|
||||||
return 'OpenClaw';
|
return 'OpenClaw';
|
||||||
}
|
}
|
||||||
@@ -462,10 +452,6 @@ const getAgentTypeIcon = (agentType: AI.AgentType) => {
|
|||||||
return copawIcon;
|
return copawIcon;
|
||||||
case 'hermes-agent':
|
case 'hermes-agent':
|
||||||
return hermesIcon;
|
return hermesIcon;
|
||||||
case 'deepseek-harness':
|
|
||||||
return deepSeekIcon;
|
|
||||||
case 'octop':
|
|
||||||
return octopIcon;
|
|
||||||
default:
|
default:
|
||||||
return openclawIcon;
|
return openclawIcon;
|
||||||
}
|
}
|
||||||
@@ -475,7 +461,8 @@ const openCreate = (agentType?: AI.AgentType) => {
|
|||||||
if (noApp.value) {
|
if (noApp.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targetType = isAgentType(agentType) ? agentType : 'openclaw';
|
const targetType =
|
||||||
|
agentType === 'copaw' || agentType === 'hermes-agent' || agentType === 'openclaw' ? agentType : 'openclaw';
|
||||||
if (addRef.value?.open) {
|
if (addRef.value?.open) {
|
||||||
addRef.value.open(targetType);
|
addRef.value.open(targetType);
|
||||||
}
|
}
|
||||||
@@ -486,7 +473,12 @@ const openCreateFromQuery = async () => {
|
|||||||
if (!shouldOpen) {
|
if (!shouldOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const agentType = isAgentType(route.query.agentType) ? route.query.agentType : 'openclaw';
|
const agentType =
|
||||||
|
route.query.agentType === 'copaw'
|
||||||
|
? 'copaw'
|
||||||
|
: route.query.agentType === 'hermes-agent'
|
||||||
|
? 'hermes-agent'
|
||||||
|
: 'openclaw';
|
||||||
openCreate(agentType);
|
openCreate(agentType);
|
||||||
const nextQuery = { ...route.query };
|
const nextQuery = { ...route.query };
|
||||||
delete nextQuery.open;
|
delete nextQuery.open;
|
||||||
@@ -555,12 +547,7 @@ const openTerminal = (row: AI.AgentItem) => {
|
|||||||
openTerminalDialog({
|
openTerminalDialog({
|
||||||
containerID: row.containerName,
|
containerID: row.containerName,
|
||||||
title,
|
title,
|
||||||
users:
|
users: row.agentType === 'hermes-agent' ? ['hermes', 'root'] : ['node', 'root'],
|
||||||
row.agentType === 'hermes-agent'
|
|
||||||
? ['hermes', 'root']
|
|
||||||
: row.agentType === 'octop'
|
|
||||||
? ['root']
|
|
||||||
: ['node', 'root'],
|
|
||||||
shell: '/bin/bash',
|
shell: '/bin/bash',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
@@ -592,14 +579,6 @@ const openWorkDir = (row: AI.AgentItem) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const jumpWebUI = (row: AI.AgentItem) => {
|
const jumpWebUI = (row: AI.AgentItem) => {
|
||||||
if (row.agentType === 'deepseek-harness') {
|
|
||||||
if (!isDeepSeekAccessHost(row.accessHost || '') || !row.webUIPort) {
|
|
||||||
MsgError(i18n.global.t('aiTools.agents.nativeAgentAccessUnavailable'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.open(`https://${row.accessHost}:${row.webUIPort}`, '_blank', 'noopener,noreferrer');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dialogPortJumpRef.value?.acceptParams) {
|
if (dialogPortJumpRef.value?.acceptParams) {
|
||||||
dialogPortJumpRef.value.acceptParams({
|
dialogPortJumpRef.value.acceptParams({
|
||||||
port: row.webUIPort,
|
port: row.webUIPort,
|
||||||
|
|||||||
@@ -73,15 +73,7 @@
|
|||||||
import { useSearchReset } from '@/composables/useSearchReset';
|
import { useSearchReset } from '@/composables/useSearchReset';
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { toRef, onMounted, reactive, ref } from 'vue';
|
import { toRef, onMounted, reactive, ref } from 'vue';
|
||||||
import {
|
import { searchApp, syncApp, syncCutomAppStore, syncLocalApp, getCurrentNodeCustomAppConfig } from '@/api/modules/app';
|
||||||
searchApp,
|
|
||||||
syncApp,
|
|
||||||
syncCutomAppStore,
|
|
||||||
syncLocalApp,
|
|
||||||
getCurrentNodeCustomAppConfig,
|
|
||||||
getLocalAppSyncConfig,
|
|
||||||
dispatchLocalAppSync,
|
|
||||||
} from '@/api/modules/app';
|
|
||||||
import Install from '../detail/install/index.vue';
|
import Install from '../detail/install/index.vue';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
@@ -96,7 +88,7 @@ import AppCard from '@/views/app-store/apps/app/index.vue';
|
|||||||
import MainDiv from '@/components/main-div/index.vue';
|
import MainDiv from '@/components/main-div/index.vue';
|
||||||
import { jumpToInstall } from '@/utils/app';
|
import { jumpToInstall } from '@/utils/app';
|
||||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||||
const { isProductPro, isOffline, isMobile, isEnterprise, isMaster, isXpackOrEE, isAdmin } = useGlobalStore();
|
const { isProductPro, isOffline, isMobile, isEnterprise } = useGlobalStore();
|
||||||
|
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
cacheSizeKey: 'app-page-size',
|
cacheSizeKey: 'app-page-size',
|
||||||
@@ -209,27 +201,21 @@ const sync = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncLocal = async () => {
|
const syncLocal = () => {
|
||||||
const taskID = newUUID();
|
const taskID = newUUID();
|
||||||
const syncReq = {
|
const syncReq = {
|
||||||
taskID: taskID,
|
taskID: taskID,
|
||||||
};
|
};
|
||||||
syncing.value = true;
|
syncing.value = true;
|
||||||
try {
|
syncLocalApp(syncReq)
|
||||||
const dispatch =
|
.then(() => {
|
||||||
isMaster.value && isXpackOrEE.value && isAdmin.value && (await getLocalAppSyncConfig()).data.enabled;
|
|
||||||
if (dispatch) {
|
|
||||||
await dispatchLocalAppSync(syncReq);
|
|
||||||
taskLogRef.value.openWithTaskID(taskID, true, 'local');
|
|
||||||
} else {
|
|
||||||
await syncLocalApp(syncReq);
|
|
||||||
openTaskLog(taskID);
|
openTaskLog(taskID);
|
||||||
}
|
canUpdate.value = false;
|
||||||
canUpdate.value = false;
|
search(req);
|
||||||
search(req);
|
})
|
||||||
} finally {
|
.finally(() => {
|
||||||
syncing.value = false;
|
syncing.value = false;
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeTag = (key: string) => {
|
const changeTag = (key: string) => {
|
||||||
|
|||||||
@@ -32,11 +32,9 @@
|
|||||||
@blur="cancelPathEditing"
|
@blur="cancelPathEditing"
|
||||||
@keyup.enter.prevent="submitPathInput"
|
@keyup.enter.prevent="submitPathInput"
|
||||||
/>
|
/>
|
||||||
<el-upload ref="uploadRef" :auto-upload="false" :show-file-list="false" :on-change="onUploadChange">
|
<el-button class="mt-2" type="primary" plain @click="onUpload">
|
||||||
<el-button class="mt-2" :loading="uploading" type="primary" plain>
|
{{ $t('commons.button.upload') }}
|
||||||
{{ $t('commons.button.upload') }}
|
</el-button>
|
||||||
</el-button>
|
|
||||||
</el-upload>
|
|
||||||
<el-button class="mt-2 ml-3" plain :disabled="selectedRows.length === 0" @click="onBatchDelete">
|
<el-button class="mt-2 ml-3" plain :disabled="selectedRows.length === 0" @click="onBatchDelete">
|
||||||
{{ $t('commons.button.delete') }}
|
{{ $t('commons.button.delete') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -97,6 +95,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</DrawerPro>
|
</DrawerPro>
|
||||||
|
<FileUpload ref="uploadRef" @close="loadContainerFiles" />
|
||||||
<DialogPro v-model="previewVisible" :title="previewTitle" size="w-70" :close-on-click-modal="true">
|
<DialogPro v-model="previewVisible" :title="previewTitle" size="w-70" :close-on-click-modal="true">
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="previewTruncated"
|
v-if="previewTruncated"
|
||||||
@@ -118,11 +117,11 @@ import {
|
|||||||
getContainerFileContent,
|
getContainerFileContent,
|
||||||
getContainerFileSize,
|
getContainerFileSize,
|
||||||
listContainerFiles,
|
listContainerFiles,
|
||||||
uploadContainerFile,
|
|
||||||
} from '@/api/modules/container';
|
} from '@/api/modules/container';
|
||||||
import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';
|
import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElInput, ElMessageBox, UploadFile, UploadInstance } from 'element-plus';
|
import { ElInput, ElMessageBox } from 'element-plus';
|
||||||
|
import FileUpload from '@/views/host/file-management/upload/index.vue';
|
||||||
import { Document, FolderOpened, HomeFilled } from '@element-plus/icons-vue';
|
import { Document, FolderOpened, HomeFilled } from '@element-plus/icons-vue';
|
||||||
import { computeSize2 } from '@/utils/size';
|
import { computeSize2 } from '@/utils/size';
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
@@ -130,9 +129,8 @@ const title = ref('');
|
|||||||
const containerID = ref('');
|
const containerID = ref('');
|
||||||
const filePath = ref('/');
|
const filePath = ref('/');
|
||||||
const containerFiles = ref<any[]>([]);
|
const containerFiles = ref<any[]>([]);
|
||||||
const uploadRef = ref<UploadInstance>();
|
const uploadRef = ref<InstanceType<typeof FileUpload>>();
|
||||||
const pathInputRef = ref<InstanceType<typeof ElInput>>();
|
const pathInputRef = ref<InstanceType<typeof ElInput>>();
|
||||||
const uploading = ref(false);
|
|
||||||
const previewVisible = ref(false);
|
const previewVisible = ref(false);
|
||||||
const previewTitle = ref('');
|
const previewTitle = ref('');
|
||||||
const previewContent = ref('');
|
const previewContent = ref('');
|
||||||
@@ -253,27 +251,8 @@ const submitPathInput = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUploadChange = async (uploadFile: UploadFile) => {
|
const onUpload = () => {
|
||||||
if (!uploadFile.raw) {
|
uploadRef.value?.acceptParams({ path: filePath.value, containerID: containerID.value });
|
||||||
return;
|
|
||||||
}
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('containerID', containerID.value);
|
|
||||||
formData.append('path', filePath.value);
|
|
||||||
formData.append('file', uploadFile.raw);
|
|
||||||
uploading.value = true;
|
|
||||||
await uploadContainerFile(formData)
|
|
||||||
.then(async () => {
|
|
||||||
MsgSuccess(i18n.global.t('file.uploadSuccess'));
|
|
||||||
await loadContainerFiles();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
uploading.value = false;
|
|
||||||
uploadRef.value?.clearFiles();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenFile = async (path: string) => {
|
const onOpenFile = async (path: string) => {
|
||||||
|
|||||||
@@ -119,8 +119,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, reactive, ref } from 'vue';
|
import { computed, onMounted, onBeforeUnmount, ref } from 'vue';
|
||||||
import { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
|
import { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile, genFileId } from 'element-plus';
|
||||||
|
import { uploadContainerFile } from '@/api/modules/container';
|
||||||
import { batchCheckFiles, stopChunkUpload, uploadFileData } from '@/api/modules/files';
|
import { batchCheckFiles, stopChunkUpload, uploadFileData } from '@/api/modules/files';
|
||||||
import { CHUNK_SIZE, uploadChunkWithRetry } from '@/utils/chunk-upload';
|
import { CHUNK_SIZE, uploadChunkWithRetry } from '@/utils/chunk-upload';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
@@ -134,6 +135,7 @@ import { useGlobalStore } from '@/composables/useGlobalStore';
|
|||||||
interface UploadFileProps {
|
interface UploadFileProps {
|
||||||
path: string;
|
path: string;
|
||||||
isAppendOnly?: boolean;
|
isAppendOnly?: boolean;
|
||||||
|
containerID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadRef = ref<UploadInstance>();
|
const uploadRef = ref<UploadInstance>();
|
||||||
@@ -154,6 +156,8 @@ const uploadOverallPercent = computed(() => {
|
|||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
const isAppendOnly = ref(false);
|
const isAppendOnly = ref(false);
|
||||||
const path = ref();
|
const path = ref();
|
||||||
|
const containerID = ref('');
|
||||||
|
const targetNode = ref('');
|
||||||
let uploadHelper = ref('');
|
let uploadHelper = ref('');
|
||||||
const dialogExistFileRef = ref();
|
const dialogExistFileRef = ref();
|
||||||
const abortController = ref<AbortController | null>(null);
|
const abortController = ref<AbortController | null>(null);
|
||||||
@@ -213,9 +217,6 @@ const closePage = () => {
|
|||||||
clearFiles();
|
clearFiles();
|
||||||
em('close', false);
|
em('close', false);
|
||||||
};
|
};
|
||||||
const state = reactive({
|
|
||||||
uploadEle: null,
|
|
||||||
});
|
|
||||||
const uploaderFiles = ref<UploadFiles>([]);
|
const uploaderFiles = ref<UploadFiles>([]);
|
||||||
const hoverIndex = ref<number | null>(null);
|
const hoverIndex = ref<number | null>(null);
|
||||||
const tmpFiles = ref<UploadFiles>([]);
|
const tmpFiles = ref<UploadFiles>([]);
|
||||||
@@ -225,9 +226,11 @@ const upload = (command: string) => {
|
|||||||
if (uploadLocked.value) {
|
if (uploadLocked.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.uploadEle.webkitdirectory = command == 'dir';
|
const input = uploadRef.value?.$el.querySelector('input[type=file]') as HTMLInputElement | null;
|
||||||
uploadRef.value.$el.querySelector('input').value = '';
|
if (!input) return;
|
||||||
uploadRef.value.$el.querySelector('input').click();
|
input.webkitdirectory = command === 'dir';
|
||||||
|
input.value = '';
|
||||||
|
input.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = (index: number) => {
|
const removeFile = (index: number) => {
|
||||||
@@ -267,7 +270,7 @@ const handleDrop = async (event: DragEvent) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const convertFileToUploadFile = (file: File, path: string): UploadFile => {
|
const convertFileToUploadFile = (file: File, path: string): UploadFile => {
|
||||||
const uid = Date.now();
|
const uid = genFileId();
|
||||||
|
|
||||||
const uploadRawFile: UploadRawFile = new File([file], file.name, {
|
const uploadRawFile: UploadRawFile = new File([file], file.name, {
|
||||||
type: file.type,
|
type: file.type,
|
||||||
@@ -294,12 +297,15 @@ const traverseFileTree = async (item: any, path = '') => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (item.isFile) {
|
if (item.isFile) {
|
||||||
if (tmpFiles.value.length > 1000) {
|
if (uploaderFiles.value.length + tmpFiles.value.length >= 1000) {
|
||||||
breakFlag.value = true;
|
breakFlag.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
item.file((file: File) => {
|
item.file((file: File) => {
|
||||||
|
if (uploaderFiles.value.length + tmpFiles.value.length >= 1000) {
|
||||||
|
breakFlag.value = true;
|
||||||
|
}
|
||||||
if (!breakFlag.value) {
|
if (!breakFlag.value) {
|
||||||
tmpFiles.value.push(convertFileToUploadFile(file, path));
|
tmpFiles.value.push(convertFileToUploadFile(file, path));
|
||||||
}
|
}
|
||||||
@@ -365,7 +371,14 @@ const handleSuccess: UploadProps['onSuccess'] = (res, file) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const files = uploaderFiles.value.slice();
|
if (uploadLocked.value) return;
|
||||||
|
const files = containerID.value
|
||||||
|
? uploaderFiles.value.filter((file) => file.status !== 'success')
|
||||||
|
: uploaderFiles.value.slice();
|
||||||
|
if (containerID.value) {
|
||||||
|
await uploadFile(files, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const fileNamesWithPath = Array.from(
|
const fileNamesWithPath = Array.from(
|
||||||
new Set(files.map((file) => `${path.value}/${file.raw.webkitRelativePath || file.name}`)),
|
new Set(files.map((file) => `${path.value}/${file.raw.webkitRelativePath || file.name}`)),
|
||||||
);
|
);
|
||||||
@@ -410,7 +423,8 @@ const uploadFile = async (files: any[], overwrite: boolean) => {
|
|||||||
upLoading.value = true;
|
upLoading.value = true;
|
||||||
uploadTotalCount.value = files.length;
|
uploadTotalCount.value = files.length;
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const uploadNode = currentNode.value;
|
const uploadNode = containerID.value ? targetNode.value : currentNode.value;
|
||||||
|
const uploadContainerID = containerID.value;
|
||||||
abortController.value = controller;
|
abortController.value = controller;
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
try {
|
try {
|
||||||
@@ -426,10 +440,27 @@ const uploadFile = async (files: any[], overwrite: boolean) => {
|
|||||||
|
|
||||||
let isSuccess = false;
|
let isSuccess = false;
|
||||||
try {
|
try {
|
||||||
isSuccess =
|
if (uploadContainerID) {
|
||||||
Number(file.size) === 0
|
const data = new FormData();
|
||||||
? await uploadEmptyFile(file, controller, uploadNode, overwrite)
|
data.append('containerID', uploadContainerID);
|
||||||
: await uploadChunkedFile(file, controller, uploadNode, overwrite);
|
data.append('path', getUploadPath(file));
|
||||||
|
data.append('file', file.raw, getFilenameFromPath(file.raw.webkitRelativePath || file.name));
|
||||||
|
await uploadContainerFile(data, {
|
||||||
|
headers: { CurrentNode: uploadNode },
|
||||||
|
signal: controller.signal,
|
||||||
|
skipErrorMessage: true,
|
||||||
|
timeout: 0,
|
||||||
|
onUploadProgress: ({ loaded, total }) => {
|
||||||
|
uploadPercent.value = total ? Math.round((loaded / total) * 100) : 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
isSuccess = true;
|
||||||
|
} else {
|
||||||
|
isSuccess =
|
||||||
|
Number(file.size) === 0
|
||||||
|
? await uploadEmptyFile(file, controller, uploadNode, overwrite)
|
||||||
|
: await uploadChunkedFile(file, controller, uploadNode, overwrite);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!controller.signal.aborted) {
|
if (!controller.signal.aborted) {
|
||||||
MsgError(getErrorMessage(error));
|
MsgError(getErrorMessage(error));
|
||||||
@@ -573,6 +604,9 @@ const getFilenameFromPath = (path: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const acceptParams = (props: UploadFileProps) => {
|
const acceptParams = (props: UploadFileProps) => {
|
||||||
|
if (uploadLocked.value) return;
|
||||||
|
containerID.value = props.containerID || '';
|
||||||
|
targetNode.value = currentNode.value;
|
||||||
path.value = props.path;
|
path.value = props.path;
|
||||||
isAppendOnly.value = Boolean(props.isAppendOnly);
|
isAppendOnly.value = Boolean(props.isAppendOnly);
|
||||||
open.value = true;
|
open.value = true;
|
||||||
@@ -581,22 +615,19 @@ const acceptParams = (props: UploadFileProps) => {
|
|||||||
uploadCurrentIndex.value = 0;
|
uploadCurrentIndex.value = 0;
|
||||||
uploadHelper.value = '';
|
uploadHelper.value = '';
|
||||||
canceling.value = false;
|
canceling.value = false;
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
state.uploadEle = document.querySelector('.el-upload__input');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
const beforeUnload = (event: BeforeUnloadEvent) => {
|
||||||
window.addEventListener('beforeunload', (e) => {
|
if (upLoading.value) {
|
||||||
if (upLoading.value) {
|
event.preventDefault();
|
||||||
if (upLoading.value) {
|
event.returnValue = i18n.global.t('file.cancelUploadHelper');
|
||||||
e.preventDefault();
|
}
|
||||||
e.returnValue = i18n.global.t('file.cancelUploadHelper');
|
};
|
||||||
return i18n.global.t('file.cancelUploadHelper');
|
onMounted(() => window.addEventListener('beforeunload', beforeUnload));
|
||||||
}
|
onBeforeUnmount(() => {
|
||||||
}
|
window.removeEventListener('beforeunload', beforeUnload);
|
||||||
});
|
abortController.value?.abort();
|
||||||
|
void cleanupActiveChunkUpload();
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({ acceptParams, handleDrop, open });
|
defineExpose({ acceptParams, handleDrop, open });
|
||||||
|
|||||||
Reference in New Issue
Block a user