Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
## [0.0.53](https://github.com/mobile-next/mobilecli/releases/tag/0.0.53) (2026-01-28)

* iOS: Fixed a bug where tcp forwarders were created on every request for mjpeg ([#158](https://github.com/mobile-next/mobilecli/pull/158))
* iOS: Lazily creating mjpeg tcp forwarder only when needed ([#158](https://github.com/mobile-next/mobilecli/pull/158))
* iOS: Multiple real devices would overwrite each other's wda tcp forwarder ([#158](https://github.com/mobile-next/mobilecli/pull/158))
* iOS: Keep running WebdriverAgent xcuitest as long as "mobilecli server" is running ([#158](https://github.com/mobile-next/mobilecli/pull/158))
* iOS: Graceful shutdown and improved tunnel closing ([#158](https://github.com/mobile-next/mobilecli/pull/158))
* iOS: Calling "device info" on iOS Simulator would fail after first time ([#158](https://github.com/mobile-next/mobilecli/pull/158))

## [0.0.52](https://github.com/mobile-next/mobilecli/releases/tag/0.0.52) (2026-01-27)
* General: Websocket support for jsonrpc, single connection multiple requests ([#154](https://github.com/mobile-next/mobilecli/pull/154))
* General: Websocket support for jsonrpc at /ws, single connection multiple requests ([#154](https://github.com/mobile-next/mobilecli/pull/154))
* iOS: Detect devicekit-ios process already running to Start Broadcasting a 2nd time ([#156](https://github.com/mobile-next/mobilecli/pull/156))
* iOS: Return more elements in "dump ui", eg Button with no label ([#155](https://github.com/mobile-next/mobilecli/pull/155))

Expand Down
18 changes: 0 additions & 18 deletions cli/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"

"github.com/mobile-next/mobilecli/commands"
"github.com/mobile-next/mobilecli/devices"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -38,23 +37,6 @@ var deviceInfoCmd = &cobra.Command{
Short: "Get device info",
Long: `Get detailed information about a connected device, such as OS, version, and screen size.`,
RunE: func(cmd *cobra.Command, args []string) error {

// Find the target device
targetDevice, err := commands.FindDeviceOrAutoSelect(deviceId)
if err != nil {
response := commands.NewErrorResponse(fmt.Errorf("error finding device: %v", err))
printJson(response)
return fmt.Errorf("%s", response.Error)
}

// Start agent
err = targetDevice.StartAgent(devices.StartAgentConfig{})
if err != nil {
response := commands.NewErrorResponse(fmt.Errorf("error starting agent: %v", err))
printJson(response)
return fmt.Errorf("%s", response.Error)
}

response := commands.InfoCommand(deviceId)
printJson(response)
if response.Status == "error" {
Expand Down
1 change: 1 addition & 0 deletions cli/screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ var screencaptureCmd = &cobra.Command{
OnProgress: func(message string) {
utils.Verbose(message)
},
Hook: commands.GetShutdownHook(),
})
if err != nil {
response := commands.NewErrorResponse(fmt.Errorf("error starting agent: %v", err))
Expand Down
20 changes: 20 additions & 0 deletions commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ func NewErrorResponse(err error) *CommandResponse {
// DeviceCache provides a simple cache for devices to avoid repeated lookups
var deviceCache = make(map[string]devices.ControllableDevice)

// shutdownHook holds the shutdown hook for resource cleanup tracking.
// It is set once at application startup via SetShutdownHook and used by commands
// to register cleanup functions for graceful shutdown.
var shutdownHook *devices.ShutdownHook

// SetShutdownHook sets the global shutdown hook for resource cleanup.
// This should be called once at application startup (main.go or server.go).
// The hook is used to register cleanup functions that will be called
// during graceful shutdown (SIGINT/SIGTERM).
func SetShutdownHook(hook *devices.ShutdownHook) {
shutdownHook = hook
}

// GetShutdownHook returns the current shutdown hook.
// Returns nil if SetShutdownHook has not been called yet.
// Commands use this to register cleanup functions.
func GetShutdownHook() *devices.ShutdownHook {
return shutdownHook
}

// FindDevice finds a device by ID, using cache when possible
func FindDevice(deviceID string) (devices.ControllableDevice, error) {
if deviceID == "" {
Expand Down
4 changes: 3 additions & 1 deletion commands/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func DumpUICommand(req DumpUIRequest) *CommandResponse {
}

// Start agent if needed
err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %w", targetDevice.ID(), err))
}
Expand Down
4 changes: 3 additions & 1 deletion commands/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ func InfoCommand(deviceID string) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("error starting agent: %v", err))
}
Expand Down
24 changes: 18 additions & 6 deletions commands/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ func TapCommand(req TapRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand All @@ -87,7 +89,9 @@ func LongPressCommand(req LongPressRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand All @@ -113,7 +117,9 @@ func TextCommand(req TextRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand All @@ -139,7 +145,9 @@ func ButtonCommand(req ButtonRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand All @@ -165,7 +173,9 @@ func GestureCommand(req GestureRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand Down Expand Up @@ -202,7 +212,9 @@ func SwipeCommand(req SwipeRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand Down
8 changes: 6 additions & 2 deletions commands/orientation.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ func OrientationGetCommand(req OrientationGetRequest) *CommandResponse {
}

// start agent if needed
err = device.StartAgent(devices.StartAgentConfig{})
err = device.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", device.ID(), err))
}
Expand Down Expand Up @@ -60,7 +62,9 @@ func OrientationSetCommand(req OrientationSetRequest) *CommandResponse {
}

// start agent if needed
err = device.StartAgent(devices.StartAgentConfig{})
err = device.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", device.ID(), err))
}
Expand Down
4 changes: 3 additions & 1 deletion commands/screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ func ScreenshotCommand(req ScreenshotRequest) *CommandResponse {
}

// Start agent if needed
err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand Down
4 changes: 3 additions & 1 deletion commands/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ func URLCommand(req URLRequest) *CommandResponse {
return NewErrorResponse(fmt.Errorf("error finding device: %v", err))
}

err = targetDevice.StartAgent(devices.StartAgentConfig{})
err = targetDevice.StartAgent(devices.StartAgentConfig{
Hook: GetShutdownHook(),
})
if err != nil {
return NewErrorResponse(fmt.Errorf("failed to start agent on device %s: %v", targetDevice.ID(), err))
}
Expand Down
1 change: 1 addition & 0 deletions devices/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type ScreenCaptureConfig struct {
// StartAgentConfig contains configuration for agent startup operations
type StartAgentConfig struct {
OnProgress func(message string) // optional progress callback
Hook *ShutdownHook // optional shutdown hook for cleanup tracking
}

// ScreenElementRect represents the rectangle coordinates and dimensions
Expand Down
Loading
Loading