mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-21 00:24:12 +08:00
241 lines
5.6 KiB
Go
241 lines
5.6 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
)
|
|
|
|
const (
|
|
openclawCronJobsRelativePath = "data/conf/cron/jobs.json"
|
|
openclawSessionStoreRelativeGlob = "data/conf/agents/*/sessions/sessions.json"
|
|
openclawSessionFilesRelativeGlob = "data/conf/agents/*/sessions/*.jsonl"
|
|
)
|
|
|
|
func (a AgentService) GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error) {
|
|
agent, install, conf, err := a.loadOpenclawAgentConfig(req.AgentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
overview := &dto.AgentOverview{
|
|
Snapshot: dto.AgentOverviewSnapshot{
|
|
ContainerStatus: install.Status,
|
|
AppVersion: install.Version,
|
|
DefaultModel: extractOpenclawDefaultModel(conf),
|
|
ChannelCount: countOpenclawConfiguredChannels(conf),
|
|
},
|
|
}
|
|
if overview.Snapshot.DefaultModel == "" {
|
|
overview.Snapshot.DefaultModel = agent.Model
|
|
}
|
|
if install.Status != constant.StatusRunning {
|
|
return overview, nil
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(3)
|
|
go func() {
|
|
defer wg.Done()
|
|
skillCount, err := loadOpenclawOverviewSkillStats(install.ContainerName)
|
|
if err == nil {
|
|
overview.Snapshot.SkillCount = skillCount
|
|
}
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
sessionCount, err := loadOpenclawOverviewSessionCount(install.GetPath())
|
|
if err == nil {
|
|
overview.Snapshot.SessionCount = sessionCount
|
|
}
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
jobCount, err := loadOpenclawOverviewJobCount(install.GetPath())
|
|
if err == nil {
|
|
overview.Snapshot.JobCount = jobCount
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
|
|
return overview, nil
|
|
}
|
|
|
|
func extractOpenclawDefaultModel(conf map[string]interface{}) string {
|
|
agents, ok := conf["agents"].(map[string]interface{})
|
|
if !ok {
|
|
return ""
|
|
}
|
|
defaults, ok := agents["defaults"].(map[string]interface{})
|
|
if !ok {
|
|
return ""
|
|
}
|
|
model, ok := defaults["model"].(map[string]interface{})
|
|
if !ok {
|
|
return ""
|
|
}
|
|
primary, _ := model["primary"].(string)
|
|
return strings.TrimSpace(primary)
|
|
}
|
|
|
|
func countOpenclawConfiguredChannels(conf map[string]interface{}) int {
|
|
channels, ok := conf["channels"].(map[string]interface{})
|
|
if !ok {
|
|
return 0
|
|
}
|
|
count := 0
|
|
for _, value := range channels {
|
|
channel, ok := value.(map[string]interface{})
|
|
if !ok || len(channel) == 0 {
|
|
continue
|
|
}
|
|
count++
|
|
}
|
|
return count
|
|
}
|
|
|
|
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
|
|
output, err := cmd.RunDockerExecWithStdout(5*time.Minute, containerName, "openclaw", "skills", "list", "--json")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if strings.TrimSpace(output) == "" {
|
|
return 0, nil
|
|
}
|
|
skills, err := parseOpenclawSkillsList(output)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return len(skills), nil
|
|
}
|
|
|
|
func loadOpenclawOverviewSessionCount(installPath string) (int, error) {
|
|
sessionStorePaths, err := filepath.Glob(filepath.Join(installPath, openclawSessionStoreRelativeGlob))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
total := 0
|
|
for _, sessionStorePath := range sessionStorePaths {
|
|
count, err := loadOpenclawSessionCountFromFile(sessionStorePath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
total += count
|
|
}
|
|
if total > 0 {
|
|
return total, nil
|
|
}
|
|
sessionFiles, err := filepath.Glob(filepath.Join(installPath, openclawSessionFilesRelativeGlob))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return len(sessionFiles), nil
|
|
}
|
|
|
|
func loadOpenclawSessionCountFromFile(sessionStorePath string) (int, error) {
|
|
content, err := os.ReadFile(sessionStorePath)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return parseOpenclawSessionCount(string(content))
|
|
}
|
|
|
|
func loadOpenclawOverviewJobCount(installPath string) (int, error) {
|
|
content, err := os.ReadFile(filepath.Join(installPath, openclawCronJobsRelativePath))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return 0, nil
|
|
}
|
|
return 0, err
|
|
}
|
|
return parseOpenclawCronCount(string(content))
|
|
}
|
|
|
|
func parseOpenclawSessionCount(output string) (int, error) {
|
|
if strings.TrimSpace(output) == "" {
|
|
return 0, nil
|
|
}
|
|
var payload interface{}
|
|
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
|
|
return 0, err
|
|
}
|
|
return countOpenclawSessions(payload), nil
|
|
}
|
|
|
|
func parseOpenclawCronCount(output string) (int, error) {
|
|
if strings.TrimSpace(output) == "" {
|
|
return 0, nil
|
|
}
|
|
var payload interface{}
|
|
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
|
|
return 0, err
|
|
}
|
|
switch value := payload.(type) {
|
|
case []interface{}:
|
|
return len(value), nil
|
|
case map[string]interface{}:
|
|
if total, ok := value["total"].(float64); ok {
|
|
return int(total), nil
|
|
}
|
|
if jobs, ok := value["jobs"].([]interface{}); ok {
|
|
return len(jobs), nil
|
|
}
|
|
return len(value), nil
|
|
default:
|
|
return 0, nil
|
|
}
|
|
}
|
|
|
|
func countOpenclawSessions(payload interface{}) int {
|
|
switch value := payload.(type) {
|
|
case []interface{}:
|
|
return len(value)
|
|
case map[string]interface{}:
|
|
if count, ok := value["count"].(float64); ok {
|
|
return int(count)
|
|
}
|
|
if sessions, ok := value["sessions"]; ok {
|
|
return countOpenclawSessions(sessions)
|
|
}
|
|
count := 0
|
|
for _, item := range value {
|
|
switch typed := item.(type) {
|
|
case map[string]interface{}:
|
|
if looksLikeOpenclawSession(typed) {
|
|
count++
|
|
}
|
|
case string:
|
|
if strings.HasSuffix(strings.TrimSpace(typed), ".jsonl") {
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
return count
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func looksLikeOpenclawSession(item map[string]interface{}) bool {
|
|
if _, ok := item["sessionFile"]; ok {
|
|
return true
|
|
}
|
|
if _, ok := item["agentId"]; ok {
|
|
return true
|
|
}
|
|
if _, ok := item["lastActivityTs"]; ok {
|
|
return true
|
|
}
|
|
if _, ok := item["updatedAt"]; ok {
|
|
return true
|
|
}
|
|
return false
|
|
}
|