fix(computer): leave running apps alone and keep the launch env clean

This commit is contained in:
Kelvin Amoaba
2026-11-14 01:56:59 +00:00
parent 2c6936727b
commit 1b9a7f7ff5
3 changed files with 33 additions and 8 deletions
@@ -816,8 +816,16 @@ final class Provider {
guard let url = installedApplicationURL(query) else {
throw ProviderError.coded("app_not_found", "app '\(query)' is not running and no installed app has that name, bundle ID or path")
}
if let bundleId = Bundle(url: url)?.bundleIdentifier, blockedBundleIds.contains(bundleId) {
throw ProviderError.coded("app_blocked", "app '\(bundleId)' is blocked for safety")
if let bundleId = Bundle(url: url)?.bundleIdentifier {
if blockedBundleIds.contains(bundleId) {
throw ProviderError.coded("app_blocked", "app '\(bundleId)' is blocked for safety")
}
// Why: a path or display-name query misses the running check above, and menu bar apps aren't listed.
if let running = NSRunningApplication.runningApplications(withBundleIdentifier: bundleId)
.first(where: { !$0.isTerminated }),
let app = appByPid(running.processIdentifier) {
return renderLaunch(app, launched: false)
}
}
let pid: pid_t
do {
@@ -834,6 +842,11 @@ final class Provider {
guard let app = appByPid(pid) else {
throw ProviderError.coded("app_launch_failed", "'\(query)' exited right after it launched")
}
// Why: Launch Services reports the app once it checks in; activation, asked for or not, lands after it finishes launching.
let deadline = Date().addingTimeInterval(5)
while !app.app.isFinishedLaunching, !app.app.isTerminated, Date() < deadline {
Thread.sleep(forTimeInterval: 0.05)
}
return renderLaunch(app, launched: true)
}
+17 -6
View File
@@ -1589,12 +1589,18 @@ function Resolve-OrcaLaunchExecutable([string]$Query) {
throw "appNotFound(`"$Query`")"
}
function Wait-OrcaLaunchedWindow([int]$ProcessId, [int]$TimeoutMilliseconds) {
function Wait-OrcaLaunchedWindow([int]$ProcessId, [string]$ProcessName, [DateTime]$StartedAt, [int]$TimeoutMilliseconds) {
$deadline = [DateTime]::UtcNow.AddMilliseconds($TimeoutMilliseconds)
while ([DateTime]::UtcNow -lt $deadline) {
$process = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
if ($null -eq $process) { return $null }
if ($process.MainWindowHandle -ne 0) { return $process }
if ($null -ne $process -and $process.MainWindowHandle -ne 0) { return $process }
if ($null -eq $process) {
# Why: stubs such as Windows 11's notepad.exe start the real app under the same name and exit.
$handoff = Get-OrcaWindowProcesses | Where-Object {
$_.ProcessName -ieq $ProcessName -and $_.StartTime -ge $StartedAt
} | Select-Object -First 1
if ($null -ne $handoff) { return $handoff }
}
Start-Sleep -Milliseconds 100
}
Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
@@ -1612,8 +1618,10 @@ function Invoke-OrcaLaunchApp($Operation) {
$query = ([string]$Operation.app).Trim()
if ([string]::IsNullOrWhiteSpace($query)) { throw 'appNotFound("")' }
$processName = [System.IO.Path]::GetFileNameWithoutExtension($query)
# Why: starting a running app again usually opens a second window over the user's work.
$running = Get-OrcaWindowProcesses | Where-Object { $_.ProcessName -ieq $processName } | Select-Object -First 1
# Why: starting a running app again usually opens a second window over the user's work; tray-only apps count too.
$session = [System.Diagnostics.Process]::GetCurrentProcess().SessionId
$running = @(Get-Process -Name ([WildcardPattern]::Escape($processName)) -ErrorAction SilentlyContinue | Where-Object { $_.SessionId -eq $session }) |
Sort-Object { $_.MainWindowHandle -eq 0 } | Select-Object -First 1
if ($null -ne $running) {
Assert-OrcaProcessAllowed $running
return New-OrcaLaunchResult $running $false
@@ -1625,6 +1633,9 @@ function Invoke-OrcaLaunchApp($Operation) {
if ($name.Contains($fragment.Replace(" ", ""))) { throw "appBlocked(`"$name`")" }
}
$activate = [bool]$Operation.activate
# Why: Orca runs this script with ELECTRON_RUN_AS_NODE=1, which would start an Electron app as bare Node.
Remove-Item Env:ELECTRON_RUN_AS_NODE -ErrorAction SilentlyContinue
$startedAt = (Get-Date).AddSeconds(-1)
try {
$processId = if ($activate) {
(Start-Process -FilePath $executable -PassThru).Id
@@ -1634,7 +1645,7 @@ function Invoke-OrcaLaunchApp($Operation) {
} catch {
throw "app_launch_failed: could not launch '$query': $($_.Exception.Message)"
}
$process = Wait-OrcaLaunchedWindow $processId 10000
$process = Wait-OrcaLaunchedWindow $processId ([System.IO.Path]::GetFileNameWithoutExtension($executable)) $startedAt 10000
if ($null -eq $process) {
throw "app_launch_failed: the process started for '$query' exited; if it handed off to another process, find that one with list-apps"
}
+1
View File
@@ -3,6 +3,7 @@ export const CLI_GLOBAL_FLAGS: readonly string[] = ['help', 'json', ...CLI_GLOBA
export const CLI_BOOLEAN_FLAGS = new Set([
'accept-changed-state',
'activate',
'agent-wait',
'all',
'allow-failed-archive-hook',