...
This commit is contained in:
		| @@ -11,7 +11,7 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/mycelium_client" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/mycelium_client" | ||||
| ) | ||||
|  | ||||
| type config struct { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/mycelium_client" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/mycelium_client" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ Dedupestor is a Go package that provides a key-value store with deduplication ba | ||||
|  | ||||
| ```go | ||||
| import ( | ||||
|     "github.com/freeflowuniverse/heroagent/pkg/dedupestor" | ||||
|     "git.ourworld.tf/herocode/heroagent/pkg/dedupestor" | ||||
| ) | ||||
|  | ||||
| // Create a new dedupe store | ||||
|   | ||||
| @@ -7,8 +7,8 @@ import ( | ||||
| 	"errors" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/ourdb" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/radixtree" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/radixtree" | ||||
| ) | ||||
|  | ||||
| // MaxValueSize is the maximum allowed size for values (1MB) | ||||
|   | ||||
| @@ -18,7 +18,7 @@ The DocTree package provides functionality for managing collections of markdown | ||||
| ### Creating a DocTree | ||||
|  | ||||
| ```go | ||||
| import "github.com/freeflowuniverse/heroagent/pkg/doctree" | ||||
| import "git.ourworld.tf/herocode/heroagent/pkg/doctree" | ||||
|  | ||||
| // Create a new DocTree with a path and name | ||||
| dt, err := doctree.New("/path/to/collection", "My Collection") | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // Collection represents a collection of markdown pages and files | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| 	"github.com/yuin/goldmark" | ||||
| 	"github.com/yuin/goldmark/extension" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // Global variable to track the current DocTree instance | ||||
|   | ||||
| @@ -34,7 +34,7 @@ import ( | ||||
|     "fmt" | ||||
|     "log" | ||||
|      | ||||
|     "github.com/freeflowuniverse/heroagent/pkg/ourdb" | ||||
|     "git.ourworld.tf/herocode/heroagent/pkg/ourdb" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -78,7 +78,7 @@ import ( | ||||
|     "fmt" | ||||
|     "log" | ||||
|      | ||||
|     "github.com/freeflowuniverse/heroagent/pkg/ourdb" | ||||
|     "git.ourworld.tf/herocode/heroagent/pkg/ourdb" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ package radixtree | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/ourdb" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb" | ||||
| ) | ||||
|  | ||||
| // Node represents a node in the radix tree | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| @@ -77,7 +77,7 @@ func (h *AdminHandler) getProcessStatsJSON(c *fiber.Ctx) error { | ||||
| 	if err != nil { | ||||
| 		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ | ||||
| 			"success": false, | ||||
| 			"error": "Failed to get process stats: " + err.Error(), | ||||
| 			"error":   "Failed to get process stats: " + err.Error(), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package api | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/sal/executor" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/sal/executor" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|   | ||||
| @@ -7,9 +7,9 @@ import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| @@ -102,26 +102,26 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) { | ||||
| 	if !ok { | ||||
| 		// Try to handle the result as a map or other structure | ||||
| 		h.logger.Printf("Warning: unexpected result type from ListProcesses, trying alternative parsing") | ||||
| 		 | ||||
|  | ||||
| 		// Try to convert the result to JSON and then parse it | ||||
| 		resultJSON, err := json.Marshal(result) | ||||
| 		if err != nil { | ||||
| 			h.logger.Printf("Error marshaling result to JSON: %v", err) | ||||
| 			return nil, fmt.Errorf("failed to marshal result: %w", err) | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		var processStatuses []interfaces.ProcessStatus | ||||
| 		if err := json.Unmarshal(resultJSON, &processStatuses); err != nil { | ||||
| 			h.logger.Printf("Error unmarshaling result to ProcessStatus: %v", err) | ||||
| 			return nil, fmt.Errorf("failed to unmarshal process list result: %w", err) | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Convert to display info format | ||||
| 		displayInfoList := make([]ProcessDisplayInfo, 0, len(processStatuses)) | ||||
| 		for _, proc := range processStatuses { | ||||
| 			// Calculate uptime based on start time | ||||
| 			uptime := formatUptime(time.Since(proc.StartTime)) | ||||
| 			 | ||||
|  | ||||
| 			displayInfo := ProcessDisplayInfo{ | ||||
| 				ID:        fmt.Sprintf("%d", proc.PID), | ||||
| 				Name:      proc.Name, | ||||
| @@ -133,7 +133,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) { | ||||
| 			} | ||||
| 			displayInfoList = append(displayInfoList, displayInfo) | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Debug: Log the number of processes | ||||
| 		h.logger.Printf("Found %d processes", len(displayInfoList)) | ||||
| 		return displayInfoList, nil | ||||
| @@ -144,7 +144,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) { | ||||
| 	for _, proc := range processStatuses { | ||||
| 		// Calculate uptime based on start time | ||||
| 		uptime := formatUptime(time.Since(proc.StartTime)) | ||||
| 		 | ||||
|  | ||||
| 		displayInfo := ProcessDisplayInfo{ | ||||
| 			ID:        fmt.Sprintf("%d", proc.PID), | ||||
| 			Name:      proc.Name, | ||||
| @@ -276,7 +276,7 @@ func (h *ServiceHandler) stopService(c *fiber.Ctx) error { | ||||
| 			"error":   fmt.Sprintf("Failed to stop service: %v", err), | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Check if the result indicates success | ||||
| 	if !result.Success { | ||||
| 		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ | ||||
| @@ -331,7 +331,7 @@ func (h *ServiceHandler) restartService(c *fiber.Ctx) error { | ||||
| 			"error":   fmt.Sprintf("Failed to restart service: %v", err), | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Check if the result indicates success | ||||
| 	if !result.Success { | ||||
| 		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ | ||||
| @@ -382,7 +382,7 @@ func (h *ServiceHandler) deleteService(c *fiber.Ctx) error { | ||||
| 			"error":   fmt.Sprintf("Failed to delete service: %v", err), | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Check if the result indicates success | ||||
| 	if !result.Success { | ||||
| 		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ | ||||
|   | ||||
| @@ -12,16 +12,16 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroagent/api" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroagent/handlers" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroagent/pages" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/sal/executor" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroagent/api" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroagent/handlers" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroagent/pages" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/sal/executor" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
|  | ||||
| 	// "github.com/freeflowuniverse/heroagent/pkg/vfs/interfaces" | ||||
| 	// "github.com/freeflowuniverse/heroagent/pkg/vfs/interfaces/mock" | ||||
| 	// "git.ourworld.tf/herocode/heroagent/pkg/vfs/interfaces" | ||||
| 	// "git.ourworld.tf/herocode/heroagent/pkg/vfs/interfaces/mock" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/cors" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/logger" | ||||
| @@ -239,7 +239,7 @@ func (hl *HeroLauncher) GetUptime() string { | ||||
| func (hl *HeroLauncher) startProcessManager() error { | ||||
| 	_, filename, _, _ := runtime.Caller(0) | ||||
| 	projectRoot := filepath.Join(filepath.Dir(filename), "../..") | ||||
| 	processManagerPath := filepath.Join(projectRoot, "cmd/processmanager/main.go") | ||||
| 	processManagerPath := filepath.Join(projectRoot, "pkg/processmanager/examples/openrpc/main.go") | ||||
|  | ||||
| 	log.Printf("Starting process manager from: %s", processManagerPath) | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"log" | ||||
| 	"strconv" // Added strconv for JobID parsing | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/herojobs" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/herojobs" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/herojobs" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/herojobs" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/logger" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/logger" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| @@ -65,40 +65,40 @@ func NewLogHandler(logPath string) (*LogHandler, error) { | ||||
| type LogType string | ||||
|  | ||||
| const ( | ||||
| 	LogTypeSystem   LogType = "system" | ||||
| 	LogTypeService  LogType = "service" | ||||
| 	LogTypeJob      LogType = "job" | ||||
| 	LogTypeProcess  LogType = "process" | ||||
| 	LogTypeAll      LogType = "all"     // Special type to retrieve logs from all sources | ||||
| 	LogTypeSystem  LogType = "system" | ||||
| 	LogTypeService LogType = "service" | ||||
| 	LogTypeJob     LogType = "job" | ||||
| 	LogTypeProcess LogType = "process" | ||||
| 	LogTypeAll     LogType = "all" // Special type to retrieve logs from all sources | ||||
| ) | ||||
|  | ||||
| // GetLogs renders the logs page with logs content | ||||
| func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 	// Check which logger to use based on the log type parameter | ||||
| 	logTypeParam := c.Query("log_type", string(LogTypeSystem)) | ||||
| 	 | ||||
|  | ||||
| 	// Parse query parameters | ||||
| 	category := c.Query("category", "") | ||||
| 	logItemType := parseLogType(c.Query("type", "")) | ||||
| 	maxItems := c.QueryInt("max_items", 100) | ||||
| 	page := c.QueryInt("page", 1) | ||||
| 	itemsPerPage := 20 // Default items per page | ||||
| 	 | ||||
|  | ||||
| 	// Parse time range | ||||
| 	fromTime := parseTimeParam(c.Query("from", "")) | ||||
| 	toTime := parseTimeParam(c.Query("to", "")) | ||||
| 	 | ||||
|  | ||||
| 	// Create search arguments | ||||
| 	searchArgs := logger.SearchArgs{ | ||||
| 		Category:      category, | ||||
| 		LogType:       logItemType, | ||||
| 		MaxItems:      maxItems, | ||||
| 		Category: category, | ||||
| 		LogType:  logItemType, | ||||
| 		MaxItems: maxItems, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !fromTime.IsZero() { | ||||
| 		searchArgs.TimestampFrom = &fromTime | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !toTime.IsZero() { | ||||
| 		searchArgs.TimestampTo = &toTime | ||||
| 	} | ||||
| @@ -107,7 +107,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 	var logs []logger.LogItem | ||||
| 	var err error | ||||
| 	var logTypeTitle string | ||||
| 	 | ||||
|  | ||||
| 	// Check if we want to merge logs from all sources | ||||
| 	if LogType(logTypeParam) == LogTypeAll { | ||||
| 		// Get merged logs from all loggers | ||||
| @@ -116,7 +116,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		// Select the appropriate logger based on the log type | ||||
| 		var selectedLogger *logger.Logger | ||||
| 		 | ||||
|  | ||||
| 		switch LogType(logTypeParam) { | ||||
| 		case LogTypeService: | ||||
| 			selectedLogger = h.serviceLogger | ||||
| @@ -131,13 +131,13 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 			selectedLogger = h.systemLogger | ||||
| 			logTypeTitle = "System Logs" | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Check if the selected logger is properly initialized | ||||
| 		if selectedLogger == nil { | ||||
| 			return c.Render("admin/system/logs", fiber.Map{ | ||||
| 				"title": logTypeTitle, | ||||
| 				"error": "Logger not initialized", | ||||
| 				"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 				"title":           logTypeTitle, | ||||
| 				"error":           "Logger not initialized", | ||||
| 				"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 				"selectedLogType": logTypeParam, | ||||
| 			}) | ||||
| 		} | ||||
| @@ -149,25 +149,24 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 	// Handle search error | ||||
| 	if err != nil { | ||||
| 		return c.Render("admin/system/logs", fiber.Map{ | ||||
| 			"title": logTypeTitle, | ||||
| 			"error": err.Error(), | ||||
| 			"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 			"title":           logTypeTitle, | ||||
| 			"error":           err.Error(), | ||||
| 			"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 			"selectedLogType": logTypeParam, | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
|  | ||||
| 	// Calculate total pages | ||||
| 	totalLogs := len(logs) | ||||
| 	totalPages := (totalLogs + itemsPerPage - 1) / itemsPerPage | ||||
| 	 | ||||
|  | ||||
| 	// Apply pagination | ||||
| 	startIndex := (page - 1) * itemsPerPage | ||||
| 	endIndex := startIndex + itemsPerPage | ||||
| 	if endIndex > totalLogs { | ||||
| 		endIndex = totalLogs | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Slice logs for current page | ||||
| 	pagedLogs := logs | ||||
| 	if startIndex < totalLogs { | ||||
| @@ -175,7 +174,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		pagedLogs = []logger.LogItem{} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Convert logs to a format suitable for the UI | ||||
| 	formattedLogs := make([]fiber.Map, 0, len(pagedLogs)) | ||||
| 	for _, log := range pagedLogs { | ||||
| @@ -185,7 +184,7 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 			logTypeStr = "ERROR" | ||||
| 			logTypeClass = "log-error" | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		formattedLogs = append(formattedLogs, fiber.Map{ | ||||
| 			"timestamp": log.Timestamp.Format("2006-01-02T15:04:05"), | ||||
| 			"category":  log.Category, | ||||
| @@ -194,20 +193,20 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| 			"typeClass": logTypeClass, | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return c.Render("admin/system/logs", fiber.Map{ | ||||
| 		"title": logTypeTitle, | ||||
| 		"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 		"title":           logTypeTitle, | ||||
| 		"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 		"selectedLogType": logTypeParam, | ||||
| 		"logs": formattedLogs, | ||||
| 		"total": totalLogs, | ||||
| 		"showing": len(formattedLogs), | ||||
| 		"page": page, | ||||
| 		"totalPages": totalPages, | ||||
| 		"categoryParam": category, | ||||
| 		"typeParam": c.Query("type", ""), | ||||
| 		"fromParam": c.Query("from", ""), | ||||
| 		"toParam": c.Query("to", ""), | ||||
| 		"logs":            formattedLogs, | ||||
| 		"total":           totalLogs, | ||||
| 		"showing":         len(formattedLogs), | ||||
| 		"page":            page, | ||||
| 		"totalPages":      totalPages, | ||||
| 		"categoryParam":   category, | ||||
| 		"typeParam":       c.Query("type", ""), | ||||
| 		"fromParam":       c.Query("from", ""), | ||||
| 		"toParam":         c.Query("to", ""), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @@ -215,27 +214,27 @@ func (h *LogHandler) GetLogs(c *fiber.Ctx) error { | ||||
| func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 	// Check which logger to use based on the log type parameter | ||||
| 	logTypeParam := c.Query("log_type", string(LogTypeSystem)) | ||||
| 	 | ||||
|  | ||||
| 	// Parse query parameters | ||||
| 	category := c.Query("category", "") | ||||
| 	logItemType := parseLogType(c.Query("type", "")) | ||||
| 	maxItems := c.QueryInt("max_items", 100) | ||||
| 	 | ||||
|  | ||||
| 	// Parse time range | ||||
| 	fromTime := parseTimeParam(c.Query("from", "")) | ||||
| 	toTime := parseTimeParam(c.Query("to", "")) | ||||
| 	 | ||||
|  | ||||
| 	// Create search arguments | ||||
| 	searchArgs := logger.SearchArgs{ | ||||
| 		Category:      category, | ||||
| 		LogType:       logItemType, | ||||
| 		MaxItems:      maxItems, | ||||
| 		Category: category, | ||||
| 		LogType:  logItemType, | ||||
| 		MaxItems: maxItems, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !fromTime.IsZero() { | ||||
| 		searchArgs.TimestampFrom = &fromTime | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !toTime.IsZero() { | ||||
| 		searchArgs.TimestampTo = &toTime | ||||
| 	} | ||||
| @@ -243,7 +242,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 	// Variables for logs and error | ||||
| 	var logs []logger.LogItem | ||||
| 	var err error | ||||
| 	 | ||||
|  | ||||
| 	// Check if we want to merge logs from all sources | ||||
| 	if LogType(logTypeParam) == LogTypeAll { | ||||
| 		// Get merged logs from all loggers | ||||
| @@ -251,7 +250,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		// Select the appropriate logger based on the log type | ||||
| 		var selectedLogger *logger.Logger | ||||
| 		 | ||||
|  | ||||
| 		switch LogType(logTypeParam) { | ||||
| 		case LogTypeService: | ||||
| 			selectedLogger = h.serviceLogger | ||||
| @@ -262,7 +261,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 		default: | ||||
| 			selectedLogger = h.systemLogger | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Check if the selected logger is properly initialized | ||||
| 		if selectedLogger == nil { | ||||
| 			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ | ||||
| @@ -280,7 +279,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 			"error": err.Error(), | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Convert logs to a format suitable for the UI | ||||
| 	response := make([]fiber.Map, 0, len(logs)) | ||||
| 	for _, log := range logs { | ||||
| @@ -288,7 +287,7 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 		if log.LogType == logger.LogTypeError { | ||||
| 			logTypeStr = "ERROR" | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		response = append(response, fiber.Map{ | ||||
| 			"timestamp": log.Timestamp.Format(time.RFC3339), | ||||
| 			"category":  log.Category, | ||||
| @@ -296,9 +295,9 @@ func (h *LogHandler) GetLogsAPI(c *fiber.Ctx) error { | ||||
| 			"type":      logTypeStr, | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"logs": response, | ||||
| 		"logs":  response, | ||||
| 		"total": len(logs), | ||||
| 	}) | ||||
| } | ||||
| @@ -309,29 +308,29 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
|  | ||||
| 	// Check which logger to use based on the log type parameter | ||||
| 	logTypeParam := c.Query("log_type", string(LogTypeSystem)) | ||||
| 	 | ||||
|  | ||||
| 	// Parse query parameters | ||||
| 	category := c.Query("category", "") | ||||
| 	logItemType := parseLogType(c.Query("type", "")) | ||||
| 	maxItems := c.QueryInt("max_items", 100) | ||||
| 	page := c.QueryInt("page", 1) | ||||
| 	itemsPerPage := 20 // Default items per page | ||||
| 	 | ||||
|  | ||||
| 	// Parse time range | ||||
| 	fromTime := parseTimeParam(c.Query("from", "")) | ||||
| 	toTime := parseTimeParam(c.Query("to", "")) | ||||
| 	 | ||||
|  | ||||
| 	// Create search arguments | ||||
| 	searchArgs := logger.SearchArgs{ | ||||
| 		Category:      category, | ||||
| 		LogType:       logItemType, | ||||
| 		MaxItems:      maxItems, | ||||
| 		Category: category, | ||||
| 		LogType:  logItemType, | ||||
| 		MaxItems: maxItems, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !fromTime.IsZero() { | ||||
| 		searchArgs.TimestampFrom = &fromTime | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if !toTime.IsZero() { | ||||
| 		searchArgs.TimestampTo = &toTime | ||||
| 	} | ||||
| @@ -340,7 +339,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 	var logs []logger.LogItem | ||||
| 	var err error | ||||
| 	var logTypeTitle string | ||||
| 	 | ||||
|  | ||||
| 	// Check if we want to merge logs from all sources | ||||
| 	if LogType(logTypeParam) == LogTypeAll { | ||||
| 		// Get merged logs from all loggers | ||||
| @@ -349,7 +348,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		// Select the appropriate logger based on the log type | ||||
| 		var selectedLogger *logger.Logger | ||||
| 		 | ||||
|  | ||||
| 		switch LogType(logTypeParam) { | ||||
| 		case LogTypeService: | ||||
| 			selectedLogger = h.serviceLogger | ||||
| @@ -364,13 +363,13 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 			selectedLogger = h.systemLogger | ||||
| 			logTypeTitle = "System Logs" | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Check if the selected logger is properly initialized | ||||
| 		if selectedLogger == nil { | ||||
| 			return c.Render("admin/system/logs_fragment", fiber.Map{ | ||||
| 				"title": logTypeTitle, | ||||
| 				"error": "Logger not initialized", | ||||
| 				"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 				"title":           logTypeTitle, | ||||
| 				"error":           "Logger not initialized", | ||||
| 				"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 				"selectedLogType": logTypeParam, | ||||
| 			}) | ||||
| 		} | ||||
| @@ -382,24 +381,24 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 	// Handle search error | ||||
| 	if err != nil { | ||||
| 		return c.Render("admin/system/logs_fragment", fiber.Map{ | ||||
| 			"title": logTypeTitle, | ||||
| 			"error": err.Error(), | ||||
| 			"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 			"title":           logTypeTitle, | ||||
| 			"error":           err.Error(), | ||||
| 			"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 			"selectedLogType": logTypeParam, | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Calculate total pages | ||||
| 	totalLogs := len(logs) | ||||
| 	totalPages := (totalLogs + itemsPerPage - 1) / itemsPerPage | ||||
| 	 | ||||
|  | ||||
| 	// Apply pagination | ||||
| 	startIndex := (page - 1) * itemsPerPage | ||||
| 	endIndex := startIndex + itemsPerPage | ||||
| 	if endIndex > totalLogs { | ||||
| 		endIndex = totalLogs | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Slice logs for current page | ||||
| 	pagedLogs := logs | ||||
| 	if startIndex < totalLogs { | ||||
| @@ -407,7 +406,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		pagedLogs = []logger.LogItem{} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Convert logs to a format suitable for the UI | ||||
| 	formattedLogs := make([]fiber.Map, 0, len(pagedLogs)) | ||||
| 	for _, log := range pagedLogs { | ||||
| @@ -417,7 +416,7 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 			logTypeStr = "ERROR" | ||||
| 			logTypeClass = "log-error" | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		formattedLogs = append(formattedLogs, fiber.Map{ | ||||
| 			"timestamp": log.Timestamp.Format("2006-01-02T15:04:05"), | ||||
| 			"category":  log.Category, | ||||
| @@ -426,18 +425,18 @@ func (h *LogHandler) GetLogsFragment(c *fiber.Ctx) error { | ||||
| 			"typeClass": logTypeClass, | ||||
| 		}) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Set layout to empty to disable the layout for fragment responses | ||||
| 	return c.Render("admin/system/logs_fragment", fiber.Map{ | ||||
| 		"title": logTypeTitle, | ||||
| 		"logTypes": []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 		"title":           logTypeTitle, | ||||
| 		"logTypes":        []LogType{LogTypeAll, LogTypeSystem, LogTypeService, LogTypeJob, LogTypeProcess}, | ||||
| 		"selectedLogType": logTypeParam, | ||||
| 		"logs": formattedLogs, | ||||
| 		"total": totalLogs, | ||||
| 		"showing": len(formattedLogs), | ||||
| 		"page": page, | ||||
| 		"totalPages": totalPages, | ||||
| 		"layout": "", // Disable layout for partial template | ||||
| 		"logs":            formattedLogs, | ||||
| 		"total":           totalLogs, | ||||
| 		"showing":         len(formattedLogs), | ||||
| 		"page":            page, | ||||
| 		"totalPages":      totalPages, | ||||
| 		"layout":          "", // Disable layout for partial template | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @@ -458,12 +457,12 @@ func parseTimeParam(timeStr string) time.Time { | ||||
| 	if timeStr == "" { | ||||
| 		return time.Time{} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	t, err := time.Parse(time.RFC3339, timeStr) | ||||
| 	if err != nil { | ||||
| 		return time.Time{} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| @@ -471,10 +470,10 @@ func parseTimeParam(timeStr string) time.Time { | ||||
| func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, error) { | ||||
| 	// Create a slice to hold all logs | ||||
| 	allLogs := make([]logger.LogItem, 0) | ||||
| 	 | ||||
|  | ||||
| 	// Create a map to track errors | ||||
| 	errors := make(map[string]error) | ||||
| 	 | ||||
|  | ||||
| 	// Get logs from system logger if available | ||||
| 	if h.systemLogger != nil { | ||||
| 		systemLogs, err := h.systemLogger.Search(args) | ||||
| @@ -488,7 +487,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er | ||||
| 			allLogs = append(allLogs, systemLogs...) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get logs from service logger if available | ||||
| 	if h.serviceLogger != nil { | ||||
| 		serviceLogs, err := h.serviceLogger.Search(args) | ||||
| @@ -502,7 +501,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er | ||||
| 			allLogs = append(allLogs, serviceLogs...) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get logs from job logger if available | ||||
| 	if h.jobLogger != nil { | ||||
| 		jobLogs, err := h.jobLogger.Search(args) | ||||
| @@ -516,7 +515,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er | ||||
| 			allLogs = append(allLogs, jobLogs...) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get logs from process logger if available | ||||
| 	if h.processLogger != nil { | ||||
| 		processLogs, err := h.processLogger.Search(args) | ||||
| @@ -530,7 +529,7 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er | ||||
| 			allLogs = append(allLogs, processLogs...) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Check if we have any logs | ||||
| 	if len(allLogs) == 0 && len(errors) > 0 { | ||||
| 		// Combine error messages | ||||
| @@ -540,16 +539,16 @@ func (h *LogHandler) getMergedLogs(args logger.SearchArgs) ([]logger.LogItem, er | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("failed to retrieve logs: %s", strings.Join(errorMsgs, "; ")) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Sort logs by timestamp (newest first) | ||||
| 	sort.Slice(allLogs, func(i, j int) bool { | ||||
| 		return allLogs[i].Timestamp.After(allLogs[j].Timestamp) | ||||
| 	}) | ||||
| 	 | ||||
|  | ||||
| 	// Apply max items limit if specified | ||||
| 	if args.MaxItems > 0 && len(allLogs) > args.MaxItems { | ||||
| 		allLogs = allLogs[:args.MaxItems] | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return allLogs, nil | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| @@ -43,8 +43,8 @@ func (h *ProcessHandler) GetProcessStatsJSON(c *fiber.Ctx) error { | ||||
|  | ||||
| 	// Convert to fiber.Map for JSON response | ||||
| 	response := fiber.Map{ | ||||
| 		"total": processData.Total, | ||||
| 		"filtered": processData.Filtered, | ||||
| 		"total":     processData.Total, | ||||
| 		"filtered":  processData.Filtered, | ||||
| 		"timestamp": time.Now().Unix(), | ||||
| 	} | ||||
|  | ||||
| @@ -61,7 +61,7 @@ func (h *ProcessHandler) GetProcessStatsJSON(c *fiber.Ctx) error { | ||||
| 			"is_current":      proc.IsCurrent, | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	response["processes"] = processes | ||||
|  | ||||
| 	// Return JSON response | ||||
| @@ -127,8 +127,8 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error { | ||||
| 	// Check if StatsManager is properly initialized | ||||
| 	if h.statsManager == nil { | ||||
| 		return c.Render("admin/system/processes_data", fiber.Map{ | ||||
| 			"error":   "System error: Stats manager not initialized", | ||||
| 			"layout":  "", | ||||
| 			"error":  "System error: Stats manager not initialized", | ||||
| 			"layout": "", | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| @@ -156,7 +156,7 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error { | ||||
| 		} else { | ||||
| 			processData, err = h.statsManager.GetProcessStatsFresh(0) | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if err != nil { | ||||
| 			// Handle AJAX requests differently from regular requests | ||||
| 			isAjax := c.Get("X-Requested-With") == "XMLHttpRequest" | ||||
| @@ -165,8 +165,8 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error { | ||||
| 			} | ||||
| 			// For regular requests, render the error within the fragment | ||||
| 			return c.Render("admin/system/processes_data", fiber.Map{ | ||||
| 				"error":   "Failed to get process data: " + err.Error(), | ||||
| 				"layout":  "", | ||||
| 				"error":  "Failed to get process data: " + err.Error(), | ||||
| 				"layout": "", | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| @@ -197,9 +197,7 @@ func (h *ProcessHandler) GetProcessesData(c *fiber.Ctx) error { | ||||
| 		"processStats": processStats, | ||||
| 		"layout":       "", // Disable layout for partial template | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Return only the table HTML content directly to be injected into the processes-table-content div | ||||
| 	return c.Render("admin/system/processes_data", templateData) | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/shirou/gopsutil/v3/host" | ||||
| ) | ||||
| @@ -335,7 +335,7 @@ func (h *SystemHandler) GetProcessStatsAPI(c *fiber.Ctx) error { | ||||
| 			"is_current":      proc.IsCurrent, | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	response["processes"] = processes | ||||
|  | ||||
| 	// Return JSON response | ||||
|   | ||||
| @@ -7,8 +7,8 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroagent/handlers" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroagent/handlers" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/shirou/gopsutil/v3/host" | ||||
| ) | ||||
| @@ -68,12 +68,12 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) { | ||||
| 	// System routes | ||||
| 	admin.Get("/system/info", h.getSystemInfo) | ||||
| 	admin.Get("/system/hardware-stats", h.getHardwareStats) | ||||
| 	 | ||||
|  | ||||
| 	// Create process handler | ||||
| 	processHandler := handlers.NewProcessHandler(h.statsManager) | ||||
| 	admin.Get("/system/processes", processHandler.GetProcesses) | ||||
| 	admin.Get("/system/processes-data", processHandler.GetProcessesData) | ||||
| 	 | ||||
|  | ||||
| 	// Create log handler | ||||
| 	// Ensure log directory exists | ||||
| 	// Using the same shared logs path as process manager | ||||
| @@ -81,7 +81,7 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) { | ||||
| 	if err := os.MkdirAll(logDir, 0755); err != nil { | ||||
| 		fmt.Printf("Error creating log directory: %v\n", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	logHandler, err := handlers.NewLogHandler(logDir) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error creating log handler: %v\n", err) | ||||
| @@ -96,11 +96,11 @@ func (h *AdminHandler) RegisterRoutes(app *fiber.App) { | ||||
| 		// but it now just redirects to the main logs endpoint | ||||
| 		admin.Get("/system/logs-fragment", logHandler.GetLogsFragment) | ||||
| 		admin.Get("/system/logs-test", h.getSystemLogsTest) // Keep the test logs route | ||||
| 		 | ||||
|  | ||||
| 		// Log API endpoints | ||||
| 		app.Get("/api/logs", logHandler.GetLogsAPI) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	admin.Get("/system/settings", h.getSystemSettings) | ||||
|  | ||||
| 	// OpenRPC routes | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/herojobs" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/herojobs" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -4,14 +4,14 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| // ServiceHandler handles service-related page routes | ||||
| type ServiceHandler struct { | ||||
| 	client  *openrpc.Client | ||||
| 	logger  *log.Logger | ||||
| 	client *openrpc.Client | ||||
| 	logger *log.Logger | ||||
| } | ||||
|  | ||||
| // NewServiceHandler creates a new service handler with the provided socket path and secret | ||||
| @@ -90,7 +90,7 @@ func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) { | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Create a ProcessDisplayInfo from the map | ||||
| 		displayInfo := ProcessDisplayInfo{ | ||||
| 			ID:        fmt.Sprintf("%v", procMap["pid"]), | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| ) | ||||
|  | ||||
| // ProcessDisplayInfo represents information about a process for display purposes | ||||
| @@ -22,7 +22,7 @@ type ProcessDisplayInfo struct { | ||||
| func ConvertToDisplayInfo(info *processmanager.ProcessInfo) ProcessDisplayInfo { | ||||
| 	// Calculate uptime from start time | ||||
| 	uptime := formatUptime(time.Since(info.StartTime)) | ||||
| 	 | ||||
|  | ||||
| 	return ProcessDisplayInfo{ | ||||
| 		ID:        fmt.Sprintf("%d", info.PID), | ||||
| 		Name:      info.Name, | ||||
|   | ||||
| @@ -7,8 +7,8 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/ourdb" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // JobStatus represents the status of a job | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -35,7 +35,7 @@ Key features: | ||||
| ```go | ||||
| import ( | ||||
|     "fmt" | ||||
|     "github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
|     "git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // Create a new playbook from HeroScript text | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/herohandler" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -24,7 +24,7 @@ func main() { | ||||
| 		fmt.Printf("Failed to initialize hero handler: %v\n", err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get the default instance | ||||
| 	handler := herohandler.DefaultInstance | ||||
|  | ||||
| @@ -43,7 +43,7 @@ func main() { | ||||
| 			tcpAddressStr = *tcpAddress | ||||
| 			fmt.Printf("TCP address: %s\n", tcpAddressStr) | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		err = handler.StartTelnet(socketPathStr, tcpAddressStr) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf("Failed to start telnet server: %v\n", err) | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -3,8 +3,8 @@ package internal | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlers" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlers" | ||||
| ) | ||||
|  | ||||
| // ExampleHandler handles example actions | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/cmd/herohandler/internal" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/cmd/herohandler/internal" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -30,7 +30,7 @@ func main() { | ||||
|  | ||||
| 	// Get the command from arguments | ||||
| 	command := strings.Join(os.Args[1:], " ") | ||||
| 	 | ||||
|  | ||||
| 	// Format as proper HeroScript with !! prefix if not already prefixed | ||||
| 	script := command | ||||
| 	if !strings.HasPrefix(script, "!!") { | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/herohandler" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -92,7 +92,7 @@ func showSupportedActions() { | ||||
| 	// We need to implement this function to get supported actions | ||||
| 	// Since we can't directly access the factory field, we'll use the telnet interface | ||||
| 	script := "!!core.actions" | ||||
| 	 | ||||
|  | ||||
| 	// Try TCP first, then Unix socket if TCP fails | ||||
| 	result, err := Send(script, "localhost:8023", false) | ||||
| 	if err != nil { | ||||
| @@ -103,7 +103,7 @@ func showSupportedActions() { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fmt.Println("Supported actions by actor:") | ||||
| 	fmt.Println(result) | ||||
| } | ||||
| @@ -151,7 +151,7 @@ func Send(command string, address string, isUnixSocket bool) (string, error) { | ||||
| 				return | ||||
| 			} | ||||
| 			response.WriteString(line) | ||||
| 			 | ||||
|  | ||||
| 			// If we've received a complete response, break | ||||
| 			if strings.Contains(line, "\n") && strings.TrimSpace(line) == "" { | ||||
| 				break | ||||
| @@ -187,7 +187,7 @@ func runTestScript() { | ||||
| 		result, err = Send(script, "/tmp/hero.sock", true) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf("Unix socket connection failed: %v\n", err) | ||||
| 			 | ||||
|  | ||||
| 			// We can't directly access the factory field, so we'll just report the error | ||||
| 			fmt.Printf("Error: %v\n", err) | ||||
| 			return | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| const exampleScript = ` | ||||
|   | ||||
| @@ -16,7 +16,7 @@ The VM handler example shows how to: | ||||
| To run the example: | ||||
|  | ||||
| ```bash | ||||
| cd ~/code/github/freeflowuniverse/heroagent/pkg/handlerfactory/cmd/vmhandler | ||||
| cd ~/code/github/freeflowuniverse/herocode/heroagent/pkg/handlerfactory/cmd/vmhandler | ||||
| go run . tutorial | ||||
| #to run just the server do | ||||
| go run .  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory" | ||||
| ) | ||||
|  | ||||
| // runTutorial runs an interactive tutorial demonstrating the VM handler | ||||
| @@ -50,7 +50,7 @@ func runTutorial() { | ||||
|  | ||||
| 	// Process heroscript commands | ||||
| 	fmt.Println("\nStep 5: Process heroscript commands") | ||||
| 	 | ||||
|  | ||||
| 	// Define a VM | ||||
| 	defineScript := `!!vm.define name:'tutorial_vm' cpu:2 memory:'4GB' storage:'50GB' | ||||
|     description: 'A tutorial VM for demonstration purposes'` | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory" | ||||
| ) | ||||
|  | ||||
| // VMHandler handles VM-related actions | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/handlerfactory" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory" | ||||
| ) | ||||
|  | ||||
| // The tutorial functions are defined in tutorial.go | ||||
|   | ||||
| @@ -30,7 +30,7 @@ The Handler Factory exposes two interfaces for communication: | ||||
| to get started | ||||
|  | ||||
| ```bash | ||||
| cd /root/code/github/freeflowuniverse/heroagent/pkg/handlerfactory/herohandler/cmd | ||||
| cd /root/code/github/freeflowuniverse/herocode/heroagent/pkg/handlerfactory/herohandler/cmd | ||||
| go run . | ||||
| ``` | ||||
|  | ||||
|   | ||||
| @@ -5,8 +5,8 @@ import ( | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // Handler interface defines methods that all handlers must implement | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // HandlerFactory manages a collection of handlers | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import ( | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // ANSI color codes for terminal output | ||||
| @@ -43,23 +43,23 @@ type TelnetServer struct { | ||||
| 	sigCh        chan os.Signal | ||||
| 	onShutdown   func() | ||||
| 	// Map to store client preferences (like json formatting) | ||||
| 	clientPrefs  map[net.Conn]map[string]bool | ||||
| 	prefsMutex   sync.RWMutex | ||||
| 	clientPrefs map[net.Conn]map[string]bool | ||||
| 	prefsMutex  sync.RWMutex | ||||
| } | ||||
|  | ||||
| // NewTelnetServer creates a new telnet server | ||||
| func NewTelnetServer(factory *HandlerFactory, secrets ...string) *TelnetServer { | ||||
| 	ctx, cancel := context.WithCancel(context.Background()) | ||||
| 	return &TelnetServer{ | ||||
| 		factory:    factory, | ||||
| 		secrets:    secrets, | ||||
| 		clients:    make(map[net.Conn]bool), | ||||
| 		factory:     factory, | ||||
| 		secrets:     secrets, | ||||
| 		clients:     make(map[net.Conn]bool), | ||||
| 		clientPrefs: make(map[net.Conn]map[string]bool), | ||||
| 		running:    false, | ||||
| 		ctx:        ctx, | ||||
| 		cancel:     cancel, | ||||
| 		sigCh:      make(chan os.Signal, 1), | ||||
| 		onShutdown: func() {}, | ||||
| 		running:     false, | ||||
| 		ctx:         ctx, | ||||
| 		cancel:      cancel, | ||||
| 		sigCh:       make(chan os.Signal, 1), | ||||
| 		onShutdown:  func() {}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -205,7 +205,7 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) { | ||||
| 	ts.clientsMutex.Lock() | ||||
| 	ts.clients[conn] = false | ||||
| 	ts.clientsMutex.Unlock() | ||||
| 	 | ||||
|  | ||||
| 	// Initialize client preferences | ||||
| 	ts.prefsMutex.Lock() | ||||
| 	ts.clientPrefs[conn] = make(map[string]bool) | ||||
| @@ -284,7 +284,7 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) { | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Handle JSON format toggle | ||||
| 		if line == "!!json" { | ||||
| 			ts.prefsMutex.Lock() | ||||
| @@ -293,12 +293,12 @@ func (ts *TelnetServer) handleConnection(conn net.Conn) { | ||||
| 				prefs = make(map[string]bool) | ||||
| 				ts.clientPrefs[conn] = prefs | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Toggle JSON format preference | ||||
| 			currentSetting := prefs["json"] | ||||
| 			prefs["json"] = !currentSetting | ||||
| 			ts.prefsMutex.Unlock() | ||||
| 			 | ||||
|  | ||||
| 			if prefs["json"] { | ||||
| 				conn.Write([]byte("JSON format will be automatically added to all heroscripts.\n")) | ||||
| 			} else { | ||||
| @@ -416,7 +416,7 @@ func (ts *TelnetServer) executeHeroscript(script string, conn net.Conn, interact | ||||
| 		ts.prefsMutex.RLock() | ||||
| 		prefs, exists := ts.clientPrefs[conn] | ||||
| 		ts.prefsMutex.RUnlock() | ||||
| 		 | ||||
|  | ||||
| 		if exists && prefs["json"] { | ||||
| 			// Add format:json if not already present | ||||
| 			if !strings.Contains(script, "format:json") { | ||||
| @@ -424,7 +424,7 @@ func (ts *TelnetServer) executeHeroscript(script string, conn net.Conn, interact | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if interactive { | ||||
| 		// Format the script with colors | ||||
| 		formattedScript := formatHeroscript(script) | ||||
| @@ -466,8 +466,6 @@ func (ts *TelnetServer) addJsonFormat(script string) string { | ||||
| 	return strings.Join(lines, "\n") | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| // formatHeroscript formats heroscript with colors for console output only | ||||
| // This is not used for telnet responses, only for server-side logging | ||||
| func formatHeroscript(script string) string { | ||||
| @@ -611,12 +609,12 @@ func (ts *TelnetServer) generateHelpText(interactive bool) string { | ||||
| 		// Try to call the Help method on each handler using reflection | ||||
| 		handlerValue := reflect.ValueOf(handler) | ||||
| 		helpMethod := handlerValue.MethodByName("Help") | ||||
| 		 | ||||
|  | ||||
| 		if helpMethod.IsValid() { | ||||
| 			// Call the Help method | ||||
| 			args := []reflect.Value{reflect.ValueOf("")} | ||||
| 			result := helpMethod.Call(args) | ||||
| 			 | ||||
|  | ||||
| 			// Get the result | ||||
| 			if len(result) > 0 && result[0].Kind() == reflect.String { | ||||
| 				helpText := result[0].String() | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package handlers | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| ) | ||||
|  | ||||
| // AuthHandler handles authentication actions | ||||
|   | ||||
| @@ -5,9 +5,9 @@ import ( | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // BaseHandler provides common functionality for all handlers | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| // HandlerFactory manages a collection of handlers for processing HeroScript commands | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package herohandler | ||||
|  | ||||
| import ( | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| ) | ||||
|  | ||||
| // GetFactory returns the handler factory | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"log" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/herohandler" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/herohandler" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -4,10 +4,10 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
|  | ||||
| 	// "github.com/freeflowuniverse/heroagent/pkg/handlerfactory/heroscript/handlerfactory/fakehandler" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/processmanagerhandler" | ||||
| 	// "git.ourworld.tf/herocode/heroagent/pkg/handlerfactory/heroscript/handlerfactory/fakehandler" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/processmanagerhandler" | ||||
| ) | ||||
|  | ||||
| // HeroHandler is the main handler factory that manages all registered handlers | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package main | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/playbook" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/playbook" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -3,8 +3,8 @@ package processmanagerhandler | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlerfactory/core" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| ) | ||||
|  | ||||
| // ProcessManagerHandler handles process manager-related actions | ||||
|   | ||||
| @@ -19,7 +19,7 @@ A Go package for parsing and manipulating parameters from text in a key-value fo | ||||
|  | ||||
| ```go | ||||
| import ( | ||||
|     "github.com/freeflowuniverse/heroagent/pkg/paramsparser" | ||||
|     "git.ourworld.tf/herocode/heroagent/pkg/paramsparser" | ||||
| ) | ||||
|  | ||||
| // Create a new parser | ||||
|   | ||||
| @@ -4,7 +4,7 @@ package main | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // ParamsParser represents a parameter parser that can handle various parameter sources | ||||
| @@ -32,12 +32,12 @@ func New() *ParamsParser { | ||||
| func (p *ParamsParser) Parse(input string) error { | ||||
| 	// Normalize line endings | ||||
| 	input = strings.ReplaceAll(input, "\r\n", "\n") | ||||
| 	 | ||||
|  | ||||
| 	// Track the current state | ||||
| 	var currentKey string | ||||
| 	var currentValue strings.Builder | ||||
| 	var inMultilineString bool | ||||
| 	 | ||||
|  | ||||
| 	// Process each line | ||||
| 	lines := strings.Split(input, "\n") | ||||
| 	for i := 0; i < len(lines); i++ { | ||||
| @@ -48,12 +48,12 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 		} else { | ||||
| 			line = lines[i] | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Skip empty lines unless we're in a multiline string | ||||
| 		if line == "" && !inMultilineString { | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// If we're in a multiline string | ||||
| 		if inMultilineString { | ||||
| 			// Check if this line ends the multiline string | ||||
| @@ -71,7 +71,7 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Process the line to extract key-value pairs | ||||
| 		var processedPos int | ||||
| 		for processedPos < len(line) { | ||||
| @@ -79,62 +79,62 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 			for processedPos < len(line) && (line[processedPos] == ' ' || line[processedPos] == '\t') { | ||||
| 				processedPos++ | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if processedPos >= len(line) { | ||||
| 				break | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Find the next key by looking for a colon | ||||
| 			keyStart := processedPos | ||||
| 			colonPos := -1 | ||||
| 			 | ||||
|  | ||||
| 			for j := processedPos; j < len(line); j++ { | ||||
| 				if line[j] == ':' { | ||||
| 					colonPos = j | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if colonPos == -1 { | ||||
| 				// No colon found, skip this part | ||||
| 				break | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Extract key and use NameFix to standardize it | ||||
| 			rawKey := strings.TrimSpace(line[keyStart:colonPos]) | ||||
| 			key := tools.NameFix(rawKey) | ||||
| 			 | ||||
|  | ||||
| 			if key == "" { | ||||
| 				// Invalid key, move past the colon and continue | ||||
| 				processedPos = colonPos + 1 | ||||
| 				continue | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Move position past the colon | ||||
| 			processedPos = colonPos + 1 | ||||
| 			 | ||||
|  | ||||
| 			if processedPos >= len(line) { | ||||
| 				// End of line reached, store empty value | ||||
| 				p.params[key] = "" | ||||
| 				break | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Skip whitespace after the colon | ||||
| 			for processedPos < len(line) && (line[processedPos] == ' ' || line[processedPos] == '\t') { | ||||
| 				processedPos++ | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if processedPos >= len(line) { | ||||
| 				// End of line reached after whitespace, store empty value | ||||
| 				p.params[key] = "" | ||||
| 				break | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			// Check if the value is quoted | ||||
| 			if line[processedPos] == '\'' { | ||||
| 				// This is a quoted string | ||||
| 				processedPos++ // Skip the opening quote | ||||
| 				 | ||||
|  | ||||
| 				// Look for the closing quote | ||||
| 				quoteEnd := -1 | ||||
| 				for j := processedPos; j < len(line); j++ { | ||||
| @@ -144,7 +144,7 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
|  | ||||
| 				if quoteEnd != -1 { | ||||
| 					// Single-line quoted string | ||||
| 					value := line[processedPos:quoteEnd] | ||||
| @@ -167,12 +167,12 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 				// This is an unquoted value | ||||
| 				valueStart := processedPos | ||||
| 				valueEnd := valueStart | ||||
| 				 | ||||
|  | ||||
| 				// Find the end of the value (space or end of line) | ||||
| 				for valueEnd < len(line) && line[valueEnd] != ' ' && line[valueEnd] != '\t' { | ||||
| 					valueEnd++ | ||||
| 				} | ||||
| 				 | ||||
|  | ||||
| 				value := line[valueStart:valueEnd] | ||||
| 				// For unquoted values, use NameFix to standardize them | ||||
| 				// This handles the 'without' keyword and other special cases | ||||
| @@ -181,12 +181,12 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// If we're still in a multiline string at the end, that's an error | ||||
| 	if inMultilineString { | ||||
| 		return errors.New("unterminated multiline string") | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -196,7 +196,7 @@ func (p *ParamsParser) Parse(input string) error { | ||||
| func (p *ParamsParser) ParseString(input string) error { | ||||
| 	// Trim the input | ||||
| 	input = strings.TrimSpace(input) | ||||
| 	 | ||||
|  | ||||
| 	// Process the input to extract key-value pairs | ||||
| 	var processedPos int | ||||
| 	for processedPos < len(input) { | ||||
| @@ -204,62 +204,62 @@ func (p *ParamsParser) ParseString(input string) error { | ||||
| 		for processedPos < len(input) && (input[processedPos] == ' ' || input[processedPos] == '\t') { | ||||
| 			processedPos++ | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if processedPos >= len(input) { | ||||
| 			break | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Find the next key by looking for a colon | ||||
| 		keyStart := processedPos | ||||
| 		colonPos := -1 | ||||
| 		 | ||||
|  | ||||
| 		for j := processedPos; j < len(input); j++ { | ||||
| 			if input[j] == ':' { | ||||
| 				colonPos = j | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if colonPos == -1 { | ||||
| 			// No colon found, skip this part | ||||
| 			break | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Extract key and use NameFix to standardize it | ||||
| 		rawKey := strings.TrimSpace(input[keyStart:colonPos]) | ||||
| 		key := tools.NameFix(rawKey) | ||||
| 		 | ||||
|  | ||||
| 		if key == "" { | ||||
| 			// Invalid key, move past the colon and continue | ||||
| 			processedPos = colonPos + 1 | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Move position past the colon | ||||
| 		processedPos = colonPos + 1 | ||||
| 		 | ||||
|  | ||||
| 		if processedPos >= len(input) { | ||||
| 			// End of input reached, store empty value | ||||
| 			p.params[key] = "" | ||||
| 			break | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Skip whitespace after the colon | ||||
| 		for processedPos < len(input) && (input[processedPos] == ' ' || input[processedPos] == '\t') { | ||||
| 			processedPos++ | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if processedPos >= len(input) { | ||||
| 			// End of input reached after whitespace, store empty value | ||||
| 			p.params[key] = "" | ||||
| 			break | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		// Check if the value is quoted | ||||
| 		if input[processedPos] == '\'' { | ||||
| 			// This is a quoted string | ||||
| 			processedPos++ // Skip the opening quote | ||||
| 			 | ||||
|  | ||||
| 			// Look for the closing quote | ||||
| 			quoteEnd := -1 | ||||
| 			for j := processedPos; j < len(input); j++ { | ||||
| @@ -269,11 +269,11 @@ func (p *ParamsParser) ParseString(input string) error { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if quoteEnd == -1 { | ||||
| 				return errors.New("unterminated quoted string") | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			value := input[processedPos:quoteEnd] | ||||
| 			// For quoted values in ParseString, we can apply NameFix | ||||
| 			// since this method doesn't handle multiline strings | ||||
| @@ -286,12 +286,12 @@ func (p *ParamsParser) ParseString(input string) error { | ||||
| 			// This is an unquoted value | ||||
| 			valueStart := processedPos | ||||
| 			valueEnd := valueStart | ||||
| 			 | ||||
|  | ||||
| 			// Find the end of the value (space or end of input) | ||||
| 			for valueEnd < len(input) && input[valueEnd] != ' ' && input[valueEnd] != '\t' { | ||||
| 				valueEnd++ | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			value := input[valueStart:valueEnd] | ||||
| 			// For unquoted values, use NameFix to standardize them | ||||
| 			// This handles the 'without' keyword and other special cases | ||||
| @@ -299,7 +299,7 @@ func (p *ParamsParser) ParseString(input string) error { | ||||
| 			processedPos = valueEnd | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -364,7 +364,7 @@ func (p *ParamsParser) GetBool(key string) bool { | ||||
| 	if value == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Check for common boolean string representations | ||||
| 	value = strings.ToLower(value) | ||||
| 	return value == "true" || value == "yes" || value == "1" || value == "on" | ||||
| @@ -405,17 +405,17 @@ func (p *ParamsParser) Has(key string) bool { | ||||
| // GetAll returns all parameters as a map | ||||
| func (p *ParamsParser) GetAll() map[string]string { | ||||
| 	result := make(map[string]string) | ||||
| 	 | ||||
|  | ||||
| 	// First add defaults | ||||
| 	for k, v := range p.defaultParams { | ||||
| 		result[k] = v | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Then override with actual params | ||||
| 	for k, v := range p.params { | ||||
| 		result[k] = v | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,8 @@ package playbook | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // State represents the parser state | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroscript/paramsparser" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser" | ||||
| ) | ||||
|  | ||||
| // ActionType represents the type of action | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/heroservices/billing/models" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroservices/billing/models" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -7,9 +7,9 @@ import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/ourdb" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/data/radixtree" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/tools" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/ourdb" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/data/radixtree" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/tools" | ||||
| ) | ||||
|  | ||||
| // DBStore represents the central database store for all models | ||||
|   | ||||
| @@ -9,8 +9,7 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	openaiproxy "github.com/freeflowuniverse/heroagent/pkg/heroservices/openaiproxy" | ||||
| 	"github.com/openai/openai-go" | ||||
| 	openaiproxy "git.ourworld.tf/herocode/heroagent/pkg/heroservices/openaiproxy" | ||||
| 	"github.com/openai/openai-go/option" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/jobsmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/jobsmanager" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/logger" | ||||
| ) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/logger" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/logger" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -5,8 +5,8 @@ import ( | ||||
| 	"log" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| ) | ||||
|  | ||||
| // RunClientExample runs a complete example of using the process manager OpenRPC client | ||||
| @@ -39,10 +39,10 @@ func RunClientExample(socketPath, secret string) error { | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to list processes: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// For simplicity in this example, just log that we got a response | ||||
| 	log.Printf("Got process list response: %T", processList) | ||||
| 	 | ||||
|  | ||||
| 	// Try to handle the response in a more robust way | ||||
| 	switch v := processList.(type) { | ||||
| 	case []interface{}: | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces/openrpc" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| ) | ||||
|  | ||||
| // MockProcessManager implements the interfaces.ProcessManagerInterface for testing purposes | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager/client" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager/client" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| ) | ||||
|  | ||||
| // Client provides a client for interacting with process manager operations via RPC | ||||
|   | ||||
| @@ -4,9 +4,9 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| ) | ||||
|  | ||||
| // Handler implements the OpenRPC handlers for process manager operations | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager" | ||||
| ) | ||||
|  | ||||
| // LoadSchema loads the OpenRPC schema from the embedded JSON file | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/openrpcmanager" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager/interfaces" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/openrpcmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager/interfaces" | ||||
| ) | ||||
|  | ||||
| // Server represents the Process Manager OpenRPC server | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package interfaces | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/processmanager" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/processmanager" | ||||
| ) | ||||
|  | ||||
| // ProcessManagerInterface defines the interface for process management operations | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import ( | ||||
| 	"time" | ||||
| 	"unicode" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/logger" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/logger" | ||||
| 	"github.com/shirou/gopsutil/v3/process" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -24,7 +24,7 @@ The server implements the following Redis commands: | ||||
| ### Basic Usage | ||||
|  | ||||
| ```go | ||||
| import "github.com/freeflowuniverse/heroagent/pkg/redisserver" | ||||
| import "git.ourworld.tf/herocode/heroagent/pkg/redisserver" | ||||
|  | ||||
| // Create a new server with default configuration | ||||
| server := redisserver.NewServer(redisserver.ServerConfig{ | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/servers/redisserver" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/servers/redisserver" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/hetznerinstall" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/hetznerinstall" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -6,10 +6,10 @@ import ( | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/dependencies" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/gosp" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/postgres" | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/verification" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/dependencies" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/gosp" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/postgres" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/verification" | ||||
| ) | ||||
|  | ||||
| // Constants for PostgreSQL installation | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/builders/postgresql/postgres" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/builders/postgresql/postgres" | ||||
| ) | ||||
|  | ||||
| // Constants for Go stored procedure | ||||
| @@ -46,10 +46,10 @@ func (b *GoSPBuilder) run(cmd string, args ...string) error { | ||||
| 	fmt.Println("Running:", cmd, args) | ||||
| 	c := exec.Command(cmd, args...) | ||||
| 	// Set environment variables | ||||
| 	c.Env = append(os.Environ(),  | ||||
| 	c.Env = append(os.Environ(), | ||||
| 		"GOROOT=/usr/local/go", | ||||
| 		"GOPATH=/root/go",  | ||||
| 		"PATH=/usr/local/go/bin:" + os.Getenv("PATH")) | ||||
| 		"GOPATH=/root/go", | ||||
| 		"PATH=/usr/local/go/bin:"+os.Getenv("PATH")) | ||||
| 	c.Stdout = os.Stdout | ||||
| 	c.Stderr = os.Stderr | ||||
| 	return c.Run() | ||||
| @@ -58,7 +58,7 @@ func (b *GoSPBuilder) run(cmd string, args ...string) error { | ||||
| // Build builds a Go stored procedure | ||||
| func (b *GoSPBuilder) Build() error { | ||||
| 	fmt.Println("Building Go stored procedure...") | ||||
| 	 | ||||
|  | ||||
| 	// Use the explicitly provided Go path if available | ||||
| 	var goExePath string | ||||
| 	if b.GoPath != "" { | ||||
| @@ -74,7 +74,7 @@ func (b *GoSPBuilder) Build() error { | ||||
| 		} | ||||
| 		fmt.Printf("Using detected Go executable from: %s\n", goExePath) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if err := os.MkdirAll(b.GoSharedLibDir, 0755); err != nil { | ||||
| 		return fmt.Errorf("failed to create directory: %w", err) | ||||
| 	} | ||||
| @@ -98,27 +98,27 @@ func main() {} | ||||
|  | ||||
| 	// Use the full path to Go rather than relying on PATH | ||||
| 	fmt.Println("Running Go build with full path:", goExePath) | ||||
| 	 | ||||
|  | ||||
| 	// Show debug information | ||||
| 	fmt.Println("Environment variables that will be set:") | ||||
| 	fmt.Println("  GOROOT=/usr/local/go") | ||||
| 	fmt.Println("  GOPATH=/root/go") | ||||
| 	fmt.Println("  PATH=/usr/local/go/bin:" + os.Getenv("PATH")) | ||||
| 	 | ||||
|  | ||||
| 	// Verify that the Go executable exists before using it | ||||
| 	if _, err := os.Stat(goExePath); err != nil { | ||||
| 		return fmt.Errorf("Go executable not found at %s: %w", goExePath, err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Create the output directory if it doesn't exist | ||||
| 	outputDir := filepath.Join(b.InstallPrefix, "lib") | ||||
| 	if err := os.MkdirAll(outputDir, 0755); err != nil { | ||||
| 		return fmt.Errorf("failed to create output directory %s: %w", outputDir, err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Prepare output path | ||||
| 	outputPath := filepath.Join(outputDir, "libgosp.so") | ||||
| 	 | ||||
|  | ||||
| 	// Instead of relying on environment variables, create a wrapper shell script | ||||
| 	// that sets all required environment variables and then calls the Go executable | ||||
| 	tempDir, err := os.MkdirTemp("", "go-build-") | ||||
| @@ -126,7 +126,7 @@ func main() {} | ||||
| 		return fmt.Errorf("failed to create temp directory: %w", err) | ||||
| 	} | ||||
| 	defer os.RemoveAll(tempDir) // Clean up when done | ||||
| 	 | ||||
|  | ||||
| 	goRoot := filepath.Dir(filepath.Dir(goExePath)) // /usr/local/go | ||||
| 	wrapperScript := filepath.Join(tempDir, "go-wrapper.sh") | ||||
| 	wrapperContent := fmt.Sprintf(`#!/bin/sh | ||||
| @@ -143,25 +143,25 @@ echo "PATH=$PATH" | ||||
| echo "=== Running Go command ===" | ||||
| echo "%s $@" | ||||
| exec %s "$@" | ||||
| `,  | ||||
| 		goRoot,  | ||||
| `, | ||||
| 		goRoot, | ||||
| 		filepath.Dir(goExePath), | ||||
| 		goExePath, | ||||
| 		goExePath) | ||||
| 	 | ||||
|  | ||||
| 	// Write the wrapper script | ||||
| 	if err := os.WriteFile(wrapperScript, []byte(wrapperContent), 0755); err != nil { | ||||
| 		return fmt.Errorf("failed to write wrapper script: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fmt.Printf("Created wrapper script at %s\n", wrapperScript) | ||||
| 	 | ||||
|  | ||||
| 	// Use the wrapper script to build the Go shared library | ||||
| 	cmd := exec.Command(wrapperScript, "build", "-buildmode=c-shared", "-o", outputPath, libPath) | ||||
| 	cmd.Dir = filepath.Dir(libPath) // Set working directory to where the source file is | ||||
| 	cmd.Stdout = os.Stdout | ||||
| 	cmd.Stderr = os.Stderr | ||||
| 	 | ||||
|  | ||||
| 	fmt.Printf("Executing Go build via wrapper script\n") | ||||
| 	if err := cmd.Run(); err != nil { | ||||
| 		return fmt.Errorf("failed to build Go stored procedure: %w", err) | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/mholt/archiver/v3" | ||||
| 	"github.com/mholt/archiver/v4" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -51,14 +51,14 @@ func (g *GoInstaller) GetGoVersion() (string, error) { | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to get Go version: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Parse go version output (format: "go version go1.x.x ...") | ||||
| 	version := strings.TrimSpace(string(output)) | ||||
| 	parts := strings.Split(version, " ") | ||||
| 	if len(parts) < 3 { | ||||
| 		return "", fmt.Errorf("unexpected go version output format: %s", version) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Return just the version number without the "go" prefix | ||||
| 	return strings.TrimPrefix(parts[2], "go"), nil | ||||
| } | ||||
| @@ -77,7 +77,7 @@ func (g *GoInstaller) InstallGo() (string, error) { | ||||
| 	// Default Go installation location | ||||
| 	var installDir string = "/usr/local" | ||||
| 	var goExePath string = filepath.Join(installDir, "go", "bin", "go") | ||||
| 	 | ||||
|  | ||||
| 	// Check if Go is already installed by checking the binary directly | ||||
| 	if _, err := os.Stat(goExePath); err == nil { | ||||
| 		version, err := g.GetGoVersion() | ||||
| @@ -86,7 +86,7 @@ func (g *GoInstaller) InstallGo() (string, error) { | ||||
| 			return goExePath, nil | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Also check if Go is available in PATH as a fallback | ||||
| 	if g.IsGoInstalled() { | ||||
| 		path, err := exec.LookPath("go") | ||||
| @@ -98,31 +98,31 @@ func (g *GoInstaller) InstallGo() (string, error) { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fmt.Printf("Installing Go version %s...\n", g.Version) | ||||
| 	 | ||||
|  | ||||
| 	// Determine architecture and OS | ||||
| 	goOS := runtime.GOOS | ||||
| 	goArch := runtime.GOARCH | ||||
| 	 | ||||
|  | ||||
| 	// Construct download URL | ||||
| 	downloadURL := fmt.Sprintf("https://golang.org/dl/go%s.%s-%s.tar.gz", g.Version, goOS, goArch) | ||||
| 	 | ||||
|  | ||||
| 	// Create a temporary directory for download | ||||
| 	tempDir, err := os.MkdirTemp("", "go-install-") | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to create temporary directory: %w", err) | ||||
| 	} | ||||
| 	defer os.RemoveAll(tempDir) | ||||
| 	 | ||||
|  | ||||
| 	// Download Go tarball | ||||
| 	tarballPath := filepath.Join(tempDir, "go.tar.gz") | ||||
| 	if err := downloadFile(downloadURL, tarballPath); err != nil { | ||||
| 		return "", fmt.Errorf("failed to download Go: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Install directory - typically /usr/local for Linux/macOS | ||||
| 	 | ||||
|  | ||||
| 	// Check if existing Go installation exists and remove it | ||||
| 	existingGoDir := filepath.Join(installDir, "go") | ||||
| 	if _, err := os.Stat(existingGoDir); err == nil { | ||||
| @@ -131,34 +131,34 @@ func (g *GoInstaller) InstallGo() (string, error) { | ||||
| 			return "", fmt.Errorf("failed to remove existing Go installation: %w", err) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Extract tarball to install directory | ||||
| 	fmt.Printf("Extracting Go to %s\n", installDir) | ||||
| 	err = extractTarGz(tarballPath, installDir) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to extract Go tarball: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Verify installation | ||||
| 	var goExePathVerify = filepath.Join(installDir, "go", "bin", "go") // Use = instead of := to avoid variable shadowing | ||||
| 	 | ||||
|  | ||||
| 	// Check if the Go binary exists | ||||
| 	var statErr error | ||||
| 	_, statErr = os.Stat(goExePathVerify) | ||||
| 	if statErr != nil { | ||||
| 		return "", fmt.Errorf("Go installation failed - go executable not found at %s", goExePathVerify) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Set up environment variables | ||||
| 	fmt.Println("Setting up Go environment variables...") | ||||
| 	 | ||||
|  | ||||
| 	// Update PATH in /etc/profile | ||||
| 	profilePath := "/etc/profile" | ||||
| 	profileContent, err := os.ReadFile(profilePath) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("failed to read profile: %w", err) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Add Go bin to PATH if not already there | ||||
| 	goBinPath := filepath.Join(installDir, "go", "bin") | ||||
| 	if !strings.Contains(string(profileContent), goBinPath) { | ||||
| @@ -167,7 +167,7 @@ func (g *GoInstaller) InstallGo() (string, error) { | ||||
| 			return "", fmt.Errorf("failed to update profile: %w", err) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fmt.Printf("✅ Go %s installed successfully!\n", g.Version) | ||||
| 	return goExePath, nil | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -18,11 +18,11 @@ func main() { | ||||
| 	// Create a new stats manager with Redis connection | ||||
| 	// Create a custom configuration | ||||
| 	config := &stats.Config{ | ||||
| 		RedisAddr:     "localhost:6379", | ||||
| 		RedisPassword: "", | ||||
| 		RedisDB:       0, | ||||
| 		Debug:         false, | ||||
| 		QueueSize:     100, | ||||
| 		RedisAddr:      "localhost:6379", | ||||
| 		RedisPassword:  "", | ||||
| 		RedisDB:        0, | ||||
| 		Debug:          false, | ||||
| 		QueueSize:      100, | ||||
| 		DefaultTimeout: 5 * time.Second, | ||||
| 		ExpirationTimes: map[string]time.Duration{ | ||||
| 			"system":   60 * time.Second,  // System info expires after 60 seconds | ||||
| @@ -32,7 +32,7 @@ func main() { | ||||
| 			"hardware": 120 * time.Second, // Hardware stats expire after 2 minutes | ||||
| 		}, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	manager, err := stats.NewStatsManager(config) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error creating stats manager: %v\n", err) | ||||
| @@ -61,12 +61,12 @@ func main() { | ||||
| 		fmt.Printf("  Cores: %d\n", sysInfo.CPU.Cores) | ||||
| 		fmt.Printf("  Model: %s\n", sysInfo.CPU.ModelName) | ||||
| 		fmt.Printf("  Usage: %.1f%%\n", sysInfo.CPU.UsagePercent) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nMemory Information:") | ||||
| 		fmt.Printf("  Total: %.1f GB\n", sysInfo.Memory.Total) | ||||
| 		fmt.Printf("  Used: %.1f GB (%.1f%%)\n", sysInfo.Memory.Used, sysInfo.Memory.UsedPercent) | ||||
| 		fmt.Printf("  Free: %.1f GB\n", sysInfo.Memory.Free) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nNetwork Information:") | ||||
| 		fmt.Printf("  Upload Speed: %s\n", sysInfo.Network.UploadSpeed) | ||||
| 		fmt.Printf("  Download Speed: %s\n", sysInfo.Network.DownloadSpeed) | ||||
| @@ -81,7 +81,7 @@ func main() { | ||||
| 	} else { | ||||
| 		fmt.Printf("Found %d disks:\n", len(diskStats.Disks)) | ||||
| 		for _, disk := range diskStats.Disks { | ||||
| 			fmt.Printf("  %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",  | ||||
| 			fmt.Printf("  %s: %.1f GB total, %.1f GB free (%.1f%% used)\n", | ||||
| 				disk.Path, disk.Total, disk.Free, disk.UsedPercent) | ||||
| 		} | ||||
| 	} | ||||
| @@ -93,12 +93,12 @@ func main() { | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error getting process stats: %v\n", err) | ||||
| 	} else { | ||||
| 		fmt.Printf("Total processes: %d (showing top %d)\n",  | ||||
| 		fmt.Printf("Total processes: %d (showing top %d)\n", | ||||
| 			processStats.Total, len(processStats.Processes)) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nTop Processes by CPU Usage:") | ||||
| 		for i, proc := range processStats.Processes { | ||||
| 			fmt.Printf("  %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",  | ||||
| 			fmt.Printf("  %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n", | ||||
| 				i+1, proc.PID, proc.Name, proc.CPUPercent, proc.MemoryMB) | ||||
| 		} | ||||
| 	} | ||||
| @@ -107,10 +107,10 @@ func main() { | ||||
| 	fmt.Println("\n4. CACHING DEMONSTRATION") | ||||
| 	fmt.Println("----------------------") | ||||
| 	fmt.Println("Getting network speed multiple times (should use cache):") | ||||
| 	 | ||||
|  | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		netSpeed := manager.GetNetworkSpeedResult() | ||||
| 		fmt.Printf("  Request %d: Upload: %s, Download: %s\n",  | ||||
| 		fmt.Printf("  Request %d: Upload: %s, Download: %s\n", | ||||
| 			i+1, netSpeed.UploadSpeed, netSpeed.DownloadSpeed) | ||||
| 		time.Sleep(500 * time.Millisecond) | ||||
| 	} | ||||
| @@ -127,17 +127,17 @@ func main() { | ||||
| 	fmt.Println("--------------------------") | ||||
| 	fmt.Println("Enabling debug mode (direct fetching without cache)...") | ||||
| 	manager.Debug = true | ||||
| 	 | ||||
|  | ||||
| 	fmt.Println("Getting system info in debug mode:") | ||||
| 	debugSysInfo, err := manager.GetSystemInfo() | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error: %v\n", err) | ||||
| 	} else { | ||||
| 		fmt.Printf("  CPU Usage: %.1f%%\n", debugSysInfo.CPU.UsagePercent) | ||||
| 		fmt.Printf("  Memory Used: %.1f GB (%.1f%%)\n",  | ||||
| 		fmt.Printf("  Memory Used: %.1f GB (%.1f%%)\n", | ||||
| 			debugSysInfo.Memory.Used, debugSysInfo.Memory.UsedPercent) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Reset debug mode | ||||
| 	manager.Debug = false | ||||
|  | ||||
| @@ -148,17 +148,17 @@ func main() { | ||||
| 	for statsType, duration := range manager.Expiration { | ||||
| 		fmt.Printf("  %s: %v\n", statsType, duration) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	fmt.Println("\nChanging system stats expiration to 10 seconds...") | ||||
| 	manager.Expiration["system"] = 10 * time.Second | ||||
| 	 | ||||
|  | ||||
| 	fmt.Println("Updated expiration times:") | ||||
| 	for statsType, duration := range manager.Expiration { | ||||
| 		fmt.Printf("  %s: %v\n", statsType, duration) | ||||
| 	} | ||||
|  | ||||
| 	fmt.Println("\nDemo complete. Press Ctrl+C to exit.") | ||||
| 	 | ||||
|  | ||||
| 	// Keep the program running | ||||
| 	select {} | ||||
| } | ||||
|   | ||||
| @@ -11,27 +11,27 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/shirou/gopsutil/v3/cpu" | ||||
| 	"github.com/shirou/gopsutil/v3/process" | ||||
| ) | ||||
|  | ||||
| // TestResult stores the results of a single test run | ||||
| type TestResult struct { | ||||
| 	StartTime       time.Time | ||||
| 	EndTime         time.Time | ||||
| 	SystemInfoTime  time.Duration | ||||
| 	DiskStatsTime   time.Duration | ||||
| 	ProcessTime     time.Duration | ||||
| 	NetworkTime     time.Duration | ||||
| 	HardwareTime    time.Duration | ||||
| 	TotalTime       time.Duration | ||||
| 	UserCPU         float64 | ||||
| 	SystemCPU       float64 | ||||
| 	TotalCPU        float64 | ||||
| 	OverallCPU      float64 | ||||
| 	MemoryUsageMB   float32 | ||||
| 	NumGoroutines   int | ||||
| 	StartTime      time.Time | ||||
| 	EndTime        time.Time | ||||
| 	SystemInfoTime time.Duration | ||||
| 	DiskStatsTime  time.Duration | ||||
| 	ProcessTime    time.Duration | ||||
| 	NetworkTime    time.Duration | ||||
| 	HardwareTime   time.Duration | ||||
| 	TotalTime      time.Duration | ||||
| 	UserCPU        float64 | ||||
| 	SystemCPU      float64 | ||||
| 	TotalCPU       float64 | ||||
| 	OverallCPU     float64 | ||||
| 	MemoryUsageMB  float32 | ||||
| 	NumGoroutines  int | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| @@ -66,11 +66,11 @@ func main() { | ||||
|  | ||||
| 	// Create a new stats manager with Redis connection | ||||
| 	config := &stats.Config{ | ||||
| 		RedisAddr:     "localhost:6379", | ||||
| 		RedisPassword: "", | ||||
| 		RedisDB:       0, | ||||
| 		Debug:         false, | ||||
| 		QueueSize:     100, | ||||
| 		RedisAddr:      "localhost:6379", | ||||
| 		RedisPassword:  "", | ||||
| 		RedisDB:        0, | ||||
| 		Debug:          false, | ||||
| 		QueueSize:      100, | ||||
| 		DefaultTimeout: 5 * time.Second, | ||||
| 		ExpirationTimes: map[string]time.Duration{ | ||||
| 			"system":   60 * time.Second,  // System info expires after 60 seconds | ||||
| @@ -80,7 +80,7 @@ func main() { | ||||
| 			"hardware": 120 * time.Second, // Hardware stats expire after 2 minutes | ||||
| 		}, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	manager, err := stats.NewStatsManager(config) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error creating stats manager: %v\n", err) | ||||
| @@ -101,11 +101,11 @@ func main() { | ||||
| 	// Set up signal handling for graceful shutdown | ||||
| 	sigChan := make(chan os.Signal, 1) | ||||
| 	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||||
| 	 | ||||
|  | ||||
| 	// Create a ticker for running tests at the specified interval | ||||
| 	ticker := time.NewTicker(time.Duration(*intervalPtr) * time.Second) | ||||
| 	defer ticker.Stop() | ||||
| 	 | ||||
|  | ||||
| 	// Store the sleep duration between operations | ||||
| 	sleepDuration := time.Duration(*sleepPtr) * time.Millisecond | ||||
|  | ||||
| @@ -118,7 +118,7 @@ func main() { | ||||
|  | ||||
| 	// Store test results | ||||
| 	var results []TestResult | ||||
| 	 | ||||
|  | ||||
| 	// Print header | ||||
| 	fmt.Printf("%-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", | ||||
| 		"Start Time", "End Time", "System(ms)", "Disk(ms)", "Process(ms)", "Network(ms)", "Hardware(ms)", "Total(ms)", "UserCPU(%)", "SysCPU(%)", "TotalCPU(%)", "Memory(MB)", "Goroutines") | ||||
| @@ -131,7 +131,7 @@ func main() { | ||||
| 			// Run a test and record the results | ||||
| 			result := runTest(manager, currentProcess, sleepDuration) | ||||
| 			results = append(results, result) | ||||
| 			 | ||||
|  | ||||
| 			// Print the result | ||||
| 			fmt.Printf("%-20s %-20s %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12.2f %-12d\n", | ||||
| 				result.StartTime.Format("15:04:05.000000"), | ||||
| @@ -147,16 +147,16 @@ func main() { | ||||
| 				result.TotalCPU, | ||||
| 				result.MemoryUsageMB, | ||||
| 				result.NumGoroutines) | ||||
| 			 | ||||
|  | ||||
| 		case <-sigChan: | ||||
| 			// Calculate and print summary statistics | ||||
| 			fmt.Println("\nTest Summary:") | ||||
| 			fmt.Println(strings.Repeat("-", 50)) | ||||
| 			 | ||||
|  | ||||
| 			var totalSystemTime, totalDiskTime, totalProcessTime, totalNetworkTime, totalHardwareTime, totalTime time.Duration | ||||
| 			var totalUserCPU, totalSystemCPU, totalCombinedCPU, totalOverallCPU float64 | ||||
| 			var totalMemory float32 | ||||
| 			 | ||||
|  | ||||
| 			for _, r := range results { | ||||
| 				totalSystemTime += r.SystemInfoTime | ||||
| 				totalDiskTime += r.DiskStatsTime | ||||
| @@ -170,7 +170,7 @@ func main() { | ||||
| 				totalOverallCPU += r.OverallCPU | ||||
| 				totalMemory += r.MemoryUsageMB | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			count := float64(len(results)) | ||||
| 			if count > 0 { | ||||
| 				fmt.Printf("Average System Info Time:  %.2f ms\n", float64(totalSystemTime.Microseconds())/(count*1000)) | ||||
| @@ -185,7 +185,7 @@ func main() { | ||||
| 				fmt.Printf("Average Overall CPU:       %.2f%%\n", totalOverallCPU/count) | ||||
| 				fmt.Printf("Average Memory Usage:      %.2f MB\n", float64(totalMemory)/count) | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			fmt.Println("\nTest completed. Exiting...") | ||||
| 			return | ||||
| 		} | ||||
| @@ -196,90 +196,90 @@ func main() { | ||||
| func runTest(manager *stats.StatsManager, proc *process.Process, sleepBetweenOps time.Duration) TestResult { | ||||
| 	// Get initial CPU times for the process | ||||
| 	initialTimes, _ := proc.Times() | ||||
| 	 | ||||
|  | ||||
| 	// Get initial overall CPU usage | ||||
| 	_, _ = cpu.Percent(0, false) // Discard initial reading, we'll only use the final reading | ||||
| 	 | ||||
|  | ||||
| 	result := TestResult{ | ||||
| 		StartTime: time.Now(), | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure total time | ||||
| 	totalStart := time.Now() | ||||
| 	 | ||||
|  | ||||
| 	// Measure system info time | ||||
| 	start := time.Now() | ||||
| 	_, _ = manager.GetSystemInfo() | ||||
| 	result.SystemInfoTime = time.Since(start) | ||||
| 	 | ||||
|  | ||||
| 	// Sleep between operations if configured | ||||
| 	if sleepBetweenOps > 0 { | ||||
| 		time.Sleep(sleepBetweenOps) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure disk stats time | ||||
| 	start = time.Now() | ||||
| 	_, _ = manager.GetDiskStats() | ||||
| 	result.DiskStatsTime = time.Since(start) | ||||
| 	 | ||||
|  | ||||
| 	// Sleep between operations if configured | ||||
| 	if sleepBetweenOps > 0 { | ||||
| 		time.Sleep(sleepBetweenOps) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure process stats time | ||||
| 	start = time.Now() | ||||
| 	_, _ = manager.GetProcessStats(10) | ||||
| 	result.ProcessTime = time.Since(start) | ||||
| 	 | ||||
|  | ||||
| 	// Sleep between operations if configured | ||||
| 	if sleepBetweenOps > 0 { | ||||
| 		time.Sleep(sleepBetweenOps) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure network speed time | ||||
| 	start = time.Now() | ||||
| 	_ = manager.GetNetworkSpeedResult() | ||||
| 	result.NetworkTime = time.Since(start) | ||||
| 	 | ||||
|  | ||||
| 	// Sleep between operations if configured | ||||
| 	if sleepBetweenOps > 0 { | ||||
| 		time.Sleep(sleepBetweenOps) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure hardware stats time | ||||
| 	start = time.Now() | ||||
| 	_ = manager.GetHardwareStatsJSON() | ||||
| 	result.HardwareTime = time.Since(start) | ||||
| 	 | ||||
|  | ||||
| 	// Record total time | ||||
| 	result.TotalTime = time.Since(totalStart) | ||||
| 	result.EndTime = time.Now() | ||||
| 	 | ||||
|  | ||||
| 	// Get final CPU times for the process | ||||
| 	finalTimes, _ := proc.Times() | ||||
| 	 | ||||
|  | ||||
| 	// Calculate CPU usage for this specific operation | ||||
| 	if initialTimes != nil && finalTimes != nil { | ||||
| 		result.UserCPU = (finalTimes.User - initialTimes.User) * 100 | ||||
| 		result.SystemCPU = (finalTimes.System - initialTimes.System) * 100 | ||||
| 		result.TotalCPU = result.UserCPU + result.SystemCPU | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get overall CPU usage | ||||
| 	finalOverallCPU, _ := cpu.Percent(0, false) | ||||
| 	if len(finalOverallCPU) > 0 { | ||||
| 		result.OverallCPU = finalOverallCPU[0] | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Measure memory usage | ||||
| 	memInfo, _ := proc.MemoryInfo() | ||||
| 	if memInfo != nil { | ||||
| 		result.MemoryUsageMB = float32(memInfo.RSS) / (1024 * 1024) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Record number of goroutines | ||||
| 	result.NumGoroutines = runtime.NumGoroutine() | ||||
| 	 | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/freeflowuniverse/heroagent/pkg/system/stats" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/system/stats" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -15,21 +15,21 @@ func main() { | ||||
|  | ||||
| 	// Create a new stats manager with Redis connection | ||||
| 	config := &stats.Config{ | ||||
| 		RedisAddr:     "localhost:6379", | ||||
| 		RedisPassword: "", | ||||
| 		RedisDB:       0, | ||||
| 		Debug:         false, | ||||
| 		QueueSize:     100, | ||||
| 		RedisAddr:      "localhost:6379", | ||||
| 		RedisPassword:  "", | ||||
| 		RedisDB:        0, | ||||
| 		Debug:          false, | ||||
| 		QueueSize:      100, | ||||
| 		DefaultTimeout: 5 * time.Second, | ||||
| 		ExpirationTimes: map[string]time.Duration{ | ||||
| 			"system":   30 * time.Second,  // System info expires after 30 seconds | ||||
| 			"disk":     60 * time.Second,  // Disk info expires after 1 minute | ||||
| 			"process":  15 * time.Second,  // Process info expires after 15 seconds | ||||
| 			"network":  20 * time.Second,  // Network info expires after 20 seconds | ||||
| 			"hardware": 60 * time.Second,  // Hardware stats expire after 1 minute | ||||
| 			"system":   30 * time.Second, // System info expires after 30 seconds | ||||
| 			"disk":     60 * time.Second, // Disk info expires after 1 minute | ||||
| 			"process":  15 * time.Second, // Process info expires after 15 seconds | ||||
| 			"network":  20 * time.Second, // Network info expires after 20 seconds | ||||
| 			"hardware": 60 * time.Second, // Hardware stats expire after 1 minute | ||||
| 		}, | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	manager, err := stats.NewStatsManager(config) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error creating stats manager: %v\n", err) | ||||
| @@ -40,7 +40,7 @@ func main() { | ||||
| 	// DISK INFORMATION | ||||
| 	fmt.Println("\n1. DISK INFORMATION") | ||||
| 	fmt.Println("------------------") | ||||
| 	 | ||||
|  | ||||
| 	// Get all disk stats using the manager | ||||
| 	diskStats, err := manager.GetDiskStats() | ||||
| 	if err != nil { | ||||
| @@ -48,7 +48,7 @@ func main() { | ||||
| 	} else { | ||||
| 		fmt.Printf("Found %d disks:\n", len(diskStats.Disks)) | ||||
| 		for _, disk := range diskStats.Disks { | ||||
| 			fmt.Printf("  %s: %.1f GB total, %.1f GB free (%.1f%% used)\n",  | ||||
| 			fmt.Printf("  %s: %.1f GB total, %.1f GB free (%.1f%% used)\n", | ||||
| 				disk.Path, disk.Total, disk.Free, disk.UsedPercent) | ||||
| 		} | ||||
| 	} | ||||
| @@ -58,7 +58,7 @@ func main() { | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error getting root disk info: %v\n", err) | ||||
| 	} else { | ||||
| 		fmt.Printf("\nRoot Disk: %.1f GB total, %.1f GB free (%.1f%% used)\n",  | ||||
| 		fmt.Printf("\nRoot Disk: %.1f GB total, %.1f GB free (%.1f%% used)\n", | ||||
| 			rootDisk.Total, rootDisk.Free, rootDisk.UsedPercent) | ||||
| 	} | ||||
|  | ||||
| @@ -68,7 +68,7 @@ func main() { | ||||
| 	// SYSTEM INFORMATION | ||||
| 	fmt.Println("\n2. SYSTEM INFORMATION") | ||||
| 	fmt.Println("--------------------") | ||||
| 	 | ||||
|  | ||||
| 	// Get system info using the manager | ||||
| 	sysInfo, err := manager.GetSystemInfo() | ||||
| 	if err != nil { | ||||
| @@ -78,12 +78,12 @@ func main() { | ||||
| 		fmt.Printf("  Cores: %d\n", sysInfo.CPU.Cores) | ||||
| 		fmt.Printf("  Model: %s\n", sysInfo.CPU.ModelName) | ||||
| 		fmt.Printf("  Usage: %.1f%%\n", sysInfo.CPU.UsagePercent) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nMemory Information:") | ||||
| 		fmt.Printf("  Total: %.1f GB\n", sysInfo.Memory.Total) | ||||
| 		fmt.Printf("  Used: %.1f GB (%.1f%%)\n", sysInfo.Memory.Used, sysInfo.Memory.UsedPercent) | ||||
| 		fmt.Printf("  Free: %.1f GB\n", sysInfo.Memory.Free) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nNetwork Information:") | ||||
| 		fmt.Printf("  Upload Speed: %s\n", sysInfo.Network.UploadSpeed) | ||||
| 		fmt.Printf("  Download Speed: %s\n", sysInfo.Network.DownloadSpeed) | ||||
| @@ -98,18 +98,18 @@ func main() { | ||||
| 	// PROCESS INFORMATION | ||||
| 	fmt.Println("\n3. PROCESS INFORMATION") | ||||
| 	fmt.Println("---------------------") | ||||
| 	 | ||||
|  | ||||
| 	// Get process stats using the manager | ||||
| 	processStats, err := manager.GetProcessStats(5) // Get top 5 processes | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error getting process stats: %v\n", err) | ||||
| 	} else { | ||||
| 		fmt.Printf("Total processes: %d (showing top %d)\n",  | ||||
| 		fmt.Printf("Total processes: %d (showing top %d)\n", | ||||
| 			processStats.Total, len(processStats.Processes)) | ||||
| 		 | ||||
|  | ||||
| 		fmt.Println("\nTop Processes by CPU Usage:") | ||||
| 		for i, proc := range processStats.Processes { | ||||
| 			fmt.Printf("  %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n",  | ||||
| 			fmt.Printf("  %d. PID %d: %s (CPU: %.1f%%, Memory: %.1f MB)\n", | ||||
| 				i+1, proc.PID, proc.Name, proc.CPUPercent, proc.MemoryMB) | ||||
| 		} | ||||
| 	} | ||||
| @@ -128,7 +128,7 @@ func main() { | ||||
| 	// COMBINED STATS | ||||
| 	fmt.Println("\n4. COMBINED STATS FUNCTIONS") | ||||
| 	fmt.Println("--------------------------") | ||||
| 	 | ||||
|  | ||||
| 	// Hardware stats using the manager | ||||
| 	fmt.Println("\nHardware Stats:") | ||||
| 	hardwareStats := manager.GetHardwareStats() | ||||
| @@ -151,17 +151,17 @@ func main() { | ||||
| 	// Wait and measure network speed again | ||||
| 	fmt.Println("\nWaiting 2 seconds for another network speed measurement...") | ||||
| 	time.Sleep(2 * time.Second) | ||||
| 	 | ||||
|  | ||||
| 	// Get updated network speed using the manager | ||||
| 	updatedNetSpeed := manager.GetNetworkSpeedResult() | ||||
| 	fmt.Println("\nUpdated Network Speed:") | ||||
| 	fmt.Printf("  Upload: %s\n", updatedNetSpeed.UploadSpeed) | ||||
| 	fmt.Printf("  Download: %s\n", updatedNetSpeed.DownloadSpeed) | ||||
| 	 | ||||
|  | ||||
| 	// CACHE MANAGEMENT | ||||
| 	fmt.Println("\n5. CACHE MANAGEMENT") | ||||
| 	fmt.Println("------------------") | ||||
| 	 | ||||
|  | ||||
| 	// Force update of system stats | ||||
| 	fmt.Println("\nForcing update of system stats...") | ||||
| 	err = manager.ForceUpdate("system") | ||||
| @@ -170,7 +170,7 @@ func main() { | ||||
| 	} else { | ||||
| 		fmt.Println("System stats updated successfully") | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Get updated system info | ||||
| 	updatedSysInfo, err := manager.GetSystemInfo() | ||||
| 	if err != nil { | ||||
| @@ -178,7 +178,7 @@ func main() { | ||||
| 	} else { | ||||
| 		fmt.Println("\nUpdated CPU Usage: " + fmt.Sprintf("%.1f%%", updatedSysInfo.CPU.UsagePercent)) | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Clear cache for disk stats | ||||
| 	fmt.Println("\nClearing cache for disk stats...") | ||||
| 	err = manager.ClearCache("disk") | ||||
| @@ -187,7 +187,7 @@ func main() { | ||||
| 	} else { | ||||
| 		fmt.Println("Disk stats cache cleared successfully") | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	// Toggle debug mode | ||||
| 	fmt.Println("\nToggling debug mode (direct fetching without cache)...") | ||||
| 	manager.Debug = !manager.Debug | ||||
|   | ||||
		Reference in New Issue
	
	Block a user