import { z } from 'zod';
import { simctl } from '../executor/simctl.js';
import { resolveDevice } from '../utils/device-resolver.js';
export const launchSchema = z.object({
device: z
.string()
.describe('Device UDID, name, or "booted"'),
bundle_id: z
.string()
.describe('Bundle identifier of the app to launch'),
args: z
.array(z.string())
.optional()
.describe('Optional arguments to pass to the app'),
wait_for_debugger: z
.boolean()
.optional()
.describe('Wait for debugger to attach before launching'),
console_pty: z
.boolean()
.optional()
.describe('Connect stdout/stderr to the current terminal'),
});
export type LaunchInput = z.infer<typeof launchSchema>;
export const launchTool = {
name: 'simctl_launch',
description: 'Launch an app on a simulator',
inputSchema: launchSchema,
handler: async (input: LaunchInput) => {
const udid = resolveDevice(input.device);
const args: string[] = [];
if (input.wait_for_debugger) {
args.push('--wait-for-debugger');
}
if (input.console_pty) {
args.push('--console-pty');
}
args.push(udid, input.bundle_id);
if (input.args && input.args.length > 0) {
args.push('--', ...input.args);
}
const result = simctl('launch', args);
if (result.exitCode !== 0) {
throw new Error(`Failed to launch app: ${result.stderr || result.stdout}`);
}
// Output contains the PID on success
const pidMatch = result.stdout.match(/^(\d+)$/);
const pid = pidMatch ? pidMatch[1] : result.stdout;
return {
content: [
{
type: 'text' as const,
text: `Successfully launched ${input.bundle_id} on device ${input.device}${pid ? ` (PID: ${pid})` : ''}`,
},
],
};
},
};