From 7b2f2ed1678f21328624fa76a13ba8ed0189b065 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Tue, 28 Jul 2026 18:00:04 +0200 Subject: [PATCH 1/2] add Stop-DeviceApp command --- .../DeviceProviders/DeviceProvider.ps1 | 14 ++++++++ .../DeviceProviders/MockDeviceProvider.ps1 | 1 + .../DeviceProviders/SwitchProvider.ps1 | 14 ++++++++ app-runner/Public/Stop-DeviceApp.ps1 | 33 +++++++++++++++++++ app-runner/README.md | 1 + app-runner/SentryAppRunner.psd1 | 1 + app-runner/SentryAppRunner.psm1 | 1 + app-runner/Tests/DeviceLifecycle.Tests.ps1 | 12 +++++++ app-runner/Tests/ModuleImport.Tests.ps1 | 1 + 9 files changed, 78 insertions(+) create mode 100644 app-runner/Public/Stop-DeviceApp.ps1 diff --git a/app-runner/Private/DeviceProviders/DeviceProvider.ps1 b/app-runner/Private/DeviceProviders/DeviceProvider.ps1 index b9bafbc..f8134f5 100644 --- a/app-runner/Private/DeviceProviders/DeviceProvider.ps1 +++ b/app-runner/Private/DeviceProviders/DeviceProvider.ps1 @@ -421,6 +421,20 @@ class DeviceProvider { } } + # Stops the application currently running on the device. + # Platforms that support this configure a 'stop-app' command; the rest log a warning. + [void] StopApplication() { + Write-Debug "$($this.Platform): Stopping application" + + if (-not $this.Commands.ContainsKey('stop-app')) { + $this.LogNotImplemented('StopApplication') + return + } + + # Not giving an argument stops whatever is in the foreground. + $this.InvokeCommand('stop-app', @('')) + } + [void] TakeScreenshot([string]$OutputPath) { # If the output path includes a directory, split it up, otherwise use current directory $directory = Split-Path $OutputPath -Parent diff --git a/app-runner/Private/DeviceProviders/MockDeviceProvider.ps1 b/app-runner/Private/DeviceProviders/MockDeviceProvider.ps1 index 40c4033..441e299 100644 --- a/app-runner/Private/DeviceProviders/MockDeviceProvider.ps1 +++ b/app-runner/Private/DeviceProviders/MockDeviceProvider.ps1 @@ -29,6 +29,7 @@ class MockDeviceProvider : DeviceProvider { "reset" = @("echo", "'Mock reset'") "getstatus" = @("echo", "'Mock get status'") "launch" = @("echo", "'Mock launch {0} {1}'") + "stop-app" = @("echo", "'Mock stop app {0}'") "screenshot" = @("echo", "'Mock screenshot'") # Target management commands for testing DetectAndSetDefaultTarget() "get-default-target" = @("echo", "'Mock get-default-target'") diff --git a/app-runner/Private/DeviceProviders/SwitchProvider.ps1 b/app-runner/Private/DeviceProviders/SwitchProvider.ps1 index 5e0c819..6d64bf0 100644 --- a/app-runner/Private/DeviceProviders/SwitchProvider.ps1 +++ b/app-runner/Private/DeviceProviders/SwitchProvider.ps1 @@ -49,6 +49,7 @@ class SwitchProvider : DeviceProvider { 'detect-target' = @($this.TargetControlTool, 'detect-target --json', { $input | ConvertFrom-Json }) 'register-target' = @($this.TargetControlTool, 'register --target "{0}"') 'launch' = @($this.ApplicationRunnerTool, '"{0}" -- {1}') + 'stop-app' = @($this.TargetControlTool, 'terminate{0}') 'screenshot' = @($this.TargetControlTool, 'take-screenshot --directory "{0}" --file-name "{1}"') 'test-internet' = @($this.TargetControlTool, 'devmenu -- network confirm-internet-connection') } @@ -142,6 +143,19 @@ class SwitchProvider : DeviceProvider { Start-Sleep -Seconds 3 } + # Override StopApplication to pass --target when a target is specified. + # + # This is needed after a crash on Switch 2 (Ounce): the crashed process stays held in + # debug mode and keeps the network use request, which stops the devkit from uploading + # the crash report. Terminating it releases the request so Nintendo forwards the report. + [void] StopApplication() { + $targetArg = if (-not [string]::IsNullOrEmpty($this.Target)) { " --target $($this.Target)" } else { '' } + + Write-Debug "$($this.Platform): Terminating application$targetArg" + + $this.InvokeCommand('stop-app', @($targetArg)) + } + # Override TakeScreenshot to pass --target when a target is specified [void] TakeScreenshot([string]$OutputPath) { $directory = Split-Path $OutputPath -Parent diff --git a/app-runner/Public/Stop-DeviceApp.ps1 b/app-runner/Public/Stop-DeviceApp.ps1 new file mode 100644 index 0000000..f8a65b3 --- /dev/null +++ b/app-runner/Public/Stop-DeviceApp.ps1 @@ -0,0 +1,33 @@ +function Stop-DeviceApp { + <# + .SYNOPSIS + Stops the application running on the connected device. + + .DESCRIPTION + Terminates the application currently running on the connected device, using the + current device session. Platforms that don't support this write a warning and return. + + This is required after a crash on Nintendo Switch 2 (Ounce) devkits: the crashed + process stays held in debug mode and keeps the network use request, which prevents the + devkit from uploading the crash report. Terminating it releases the request so the + report gets forwarded. + + .EXAMPLE + Connect-Device -Platform Switch + Invoke-DeviceApp -ExecutablePath "Game.nsp" -Arguments "crash-capture" + Stop-DeviceApp + + .EXAMPLE + # Terminating may fail if nothing is running - don't let that abort the caller. + try { Stop-DeviceApp } catch { Write-Warning "Failed to stop app: $_" } + #> + [CmdletBinding()] + param() + + Assert-DeviceSession + + Write-Debug "Stopping application on platform: $($script:CurrentSession.Platform)" + + $provider = $script:CurrentSession.Provider + $provider.StopApplication() +} diff --git a/app-runner/README.md b/app-runner/README.md index c4920bd..951626b 100644 --- a/app-runner/README.md +++ b/app-runner/README.md @@ -136,6 +136,7 @@ Disconnect-Device ### App Execution - `Invoke-DeviceApp` - Install and run application (unified command) +- `Stop-DeviceApp` - Terminate the application running on the device ### Device Lifecycle - `Start-Device` - Power on device diff --git a/app-runner/SentryAppRunner.psd1 b/app-runner/SentryAppRunner.psd1 index ccc8aef..ea4557e 100644 --- a/app-runner/SentryAppRunner.psd1 +++ b/app-runner/SentryAppRunner.psd1 @@ -44,6 +44,7 @@ 'Restart-Device', 'Start-Device', 'Stop-Device', + 'Stop-DeviceApp', 'Test-DeviceConnection', 'Test-DeviceInternetConnection' ) diff --git a/app-runner/SentryAppRunner.psm1 b/app-runner/SentryAppRunner.psm1 index e8b6470..83ad4ac 100644 --- a/app-runner/SentryAppRunner.psm1 +++ b/app-runner/SentryAppRunner.psm1 @@ -64,6 +64,7 @@ Export-ModuleMember -Function @( # Application Management 'Install-DeviceApp', 'Invoke-DeviceApp', + 'Stop-DeviceApp', # Device Lifecycle 'Start-Device', diff --git a/app-runner/Tests/DeviceLifecycle.Tests.ps1 b/app-runner/Tests/DeviceLifecycle.Tests.ps1 index 0737e60..283177a 100644 --- a/app-runner/Tests/DeviceLifecycle.Tests.ps1 +++ b/app-runner/Tests/DeviceLifecycle.Tests.ps1 @@ -42,6 +42,18 @@ Context 'Stop-Device' { } } +Context 'Stop-DeviceApp' { + It 'Should require device session' { + Disconnect-Device + { Stop-DeviceApp } | Should -Throw '*No active device session*' + } + + It 'Should work with active session' { + Connect-Device -Platform 'Mock' + { Stop-DeviceApp } | Should -Not -Throw + } +} + Context 'Get-DeviceStatus' { It 'Should require device session' { Disconnect-Device diff --git a/app-runner/Tests/ModuleImport.Tests.ps1 b/app-runner/Tests/ModuleImport.Tests.ps1 index e908fa0..e9741de 100644 --- a/app-runner/Tests/ModuleImport.Tests.ps1 +++ b/app-runner/Tests/ModuleImport.Tests.ps1 @@ -33,6 +33,7 @@ Context 'Module Loading' { 'Test-DeviceConnection', 'Test-DeviceInternetConnection', 'Invoke-DeviceApp', + 'Stop-DeviceApp', # Device Lifecycle 'Start-Device', From 0a752e2d8c0761445ea44f271802d09ba7fc426a Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Tue, 28 Jul 2026 18:18:11 +0200 Subject: [PATCH 2/2] remove unnecessary comment --- app-runner/Public/Stop-DeviceApp.ps1 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app-runner/Public/Stop-DeviceApp.ps1 b/app-runner/Public/Stop-DeviceApp.ps1 index f8a65b3..cf1926d 100644 --- a/app-runner/Public/Stop-DeviceApp.ps1 +++ b/app-runner/Public/Stop-DeviceApp.ps1 @@ -7,11 +7,6 @@ function Stop-DeviceApp { Terminates the application currently running on the connected device, using the current device session. Platforms that don't support this write a warning and return. - This is required after a crash on Nintendo Switch 2 (Ounce) devkits: the crashed - process stays held in debug mode and keeps the network use request, which prevents the - devkit from uploading the crash report. Terminating it releases the request so the - report gets forwarded. - .EXAMPLE Connect-Device -Platform Switch Invoke-DeviceApp -ExecutablePath "Game.nsp" -Arguments "crash-capture"