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:
129
agent/deploy/desktop_path.go
Normal file
129
agent/deploy/desktop_path.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const desktopPathPrefix = "@desktop/"
|
||||
|
||||
// UserDesktopDir returns the interactive user's Desktop folder for the current OS.
|
||||
func UserDesktopDir() (string, error) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return windowsDesktopDir()
|
||||
case "darwin":
|
||||
return unixDesktopFromHome("Desktop")
|
||||
default:
|
||||
return linuxDesktopDir()
|
||||
}
|
||||
}
|
||||
|
||||
func windowsDesktopDir() (string, error) {
|
||||
profile := strings.TrimSpace(os.Getenv("USERPROFILE"))
|
||||
if profile == "" {
|
||||
return "", fmt.Errorf("USERPROFILE not set")
|
||||
}
|
||||
candidates := []string{
|
||||
filepath.Join(profile, "Desktop"),
|
||||
filepath.Join(profile, "OneDrive", "Desktop"),
|
||||
filepath.Join(profile, "OneDrive - Personal", "Desktop"),
|
||||
}
|
||||
for _, c := range candidates {
|
||||
if st, err := os.Stat(c); err == nil && st.IsDir() {
|
||||
return filepath.Clean(c), nil
|
||||
}
|
||||
}
|
||||
// Create default Desktop if missing (unusual profiles)
|
||||
fallback := candidates[0]
|
||||
if err := os.MkdirAll(fallback, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fallback, nil
|
||||
}
|
||||
|
||||
func unixDesktopFromHome(sub string) (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
return "", fmt.Errorf("home directory unavailable")
|
||||
}
|
||||
desktop := filepath.Join(home, sub)
|
||||
if st, err := os.Stat(desktop); err == nil && st.IsDir() {
|
||||
return filepath.Clean(desktop), nil
|
||||
}
|
||||
if err := os.MkdirAll(desktop, 0o755); err != nil {
|
||||
return "", fmt.Errorf("desktop: %w", err)
|
||||
}
|
||||
return desktop, nil
|
||||
}
|
||||
|
||||
func linuxDesktopDir() (string, error) {
|
||||
if out, err := exec.Command("xdg-user-dir", "DESKTOP").Output(); err == nil {
|
||||
p := strings.TrimSpace(string(out))
|
||||
if p != "" {
|
||||
if st, err := os.Stat(p); err == nil && st.IsDir() {
|
||||
return filepath.Clean(p), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return unixDesktopFromHome("Desktop")
|
||||
}
|
||||
|
||||
// ResolveDesktopFile joins a sanitized filename onto the user Desktop.
|
||||
func ResolveDesktopFile(filename string) (string, error) {
|
||||
desktop, err := UserDesktopDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := sanitizeDesktopFilename(filename)
|
||||
if name == "" {
|
||||
name = "upload.bin"
|
||||
}
|
||||
return filepath.Join(desktop, name), nil
|
||||
}
|
||||
|
||||
func sanitizeDesktopFilename(name string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
name = strings.ReplaceAll(name, "\\", "/")
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
// Allow subfolders under Desktop but block traversal.
|
||||
parts := strings.Split(name, "/")
|
||||
var clean []string
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p == "" || p == "." || p == ".." {
|
||||
continue
|
||||
}
|
||||
clean = append(clean, p)
|
||||
}
|
||||
return filepath.Join(clean...)
|
||||
}
|
||||
|
||||
// ResolveRemotePath expands @desktop/…, desktop:…, and ~/… for upload/download commands.
|
||||
func ResolveRemotePath(remote string) (string, error) {
|
||||
remote = strings.TrimSpace(remote)
|
||||
if remote == "" {
|
||||
return "", fmt.Errorf("remote path is empty")
|
||||
}
|
||||
lower := strings.ToLower(remote)
|
||||
if strings.HasPrefix(lower, "desktop:") {
|
||||
return ResolveDesktopFile(remote[len("desktop:"):])
|
||||
}
|
||||
if strings.HasPrefix(remote, desktopPathPrefix) {
|
||||
return ResolveDesktopFile(remote[len(desktopPathPrefix):])
|
||||
}
|
||||
if strings.HasPrefix(remote, "~/") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Clean(filepath.Join(home, remote[2:])), nil
|
||||
}
|
||||
return filepath.Clean(remote), nil
|
||||
}
|
||||
Reference in New Issue
Block a user