(
appPath: string,
executor: CommandExecutor,
opts?: { args?: string[] },
)
| 14 | * Launch a macOS app and return bundle ID and process ID if available. |
| 15 | */ |
| 16 | export async function launchMacApp( |
| 17 | appPath: string, |
| 18 | executor: CommandExecutor, |
| 19 | opts?: { args?: string[] }, |
| 20 | ): Promise<MacLaunchResult> { |
| 21 | log('info', `Launching macOS app: ${appPath}`); |
| 22 | const command = buildOpenAppCommand(appPath, { args: opts?.args }); |
| 23 | |
| 24 | const result = await executor(command, 'Launch macOS App', false); |
| 25 | if (!result.success) { |
| 26 | return { success: false, error: result.error ?? 'Failed to launch app' }; |
| 27 | } |
| 28 | |
| 29 | let bundleId: string | undefined; |
| 30 | try { |
| 31 | const plistResult = await executor( |
| 32 | ['defaults', 'read', `${appPath}/Contents/Info`, 'CFBundleIdentifier'], |
| 33 | 'Extract Bundle ID', |
| 34 | false, |
| 35 | ); |
| 36 | if (plistResult.success && plistResult.output) { |
| 37 | bundleId = plistResult.output.trim(); |
| 38 | } |
| 39 | } catch { |
| 40 | // non-fatal |
| 41 | } |
| 42 | |
| 43 | const appName = path.basename(appPath, '.app'); |
| 44 | const processId = await resolveProcessId(appName, executor); |
| 45 | |
| 46 | return { success: true, bundleId, processId }; |
| 47 | } |
| 48 | |
| 49 | const MAC_PID_TIMEOUT_MS = 2000; |
| 50 | const MAC_PID_INTERVAL_MS = 100; |
no test coverage detected