docker-flutter-ios-simulator-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CLAUDE.md +74 -0
  2. package/LICENSE +21 -0
  3. package/README.md +370 -0
  4. package/dist/flutter/log-buffer.d.ts +14 -0
  5. package/dist/flutter/log-buffer.d.ts.map +1 -0
  6. package/dist/flutter/log-buffer.js +49 -0
  7. package/dist/flutter/log-buffer.js.map +1 -0
  8. package/dist/flutter/process.d.ts +30 -0
  9. package/dist/flutter/process.d.ts.map +1 -0
  10. package/dist/flutter/process.js +241 -0
  11. package/dist/flutter/process.js.map +1 -0
  12. package/dist/flutter/types.d.ts +31 -0
  13. package/dist/flutter/types.d.ts.map +1 -0
  14. package/dist/flutter/types.js +2 -0
  15. package/dist/flutter/types.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +207 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/server.d.ts +3 -0
  21. package/dist/server.d.ts.map +1 -0
  22. package/dist/server.js +18 -0
  23. package/dist/server.js.map +1 -0
  24. package/dist/session/manager.d.ts +74 -0
  25. package/dist/session/manager.d.ts.map +1 -0
  26. package/dist/session/manager.js +209 -0
  27. package/dist/session/manager.js.map +1 -0
  28. package/dist/session/state.d.ts +14 -0
  29. package/dist/session/state.d.ts.map +1 -0
  30. package/dist/session/state.js +42 -0
  31. package/dist/session/state.js.map +1 -0
  32. package/dist/session/types.d.ts +26 -0
  33. package/dist/session/types.d.ts.map +1 -0
  34. package/dist/session/types.js +2 -0
  35. package/dist/session/types.js.map +1 -0
  36. package/dist/simulator/idb.d.ts +42 -0
  37. package/dist/simulator/idb.d.ts.map +1 -0
  38. package/dist/simulator/idb.js +127 -0
  39. package/dist/simulator/idb.js.map +1 -0
  40. package/dist/simulator/simctl.d.ts +9 -0
  41. package/dist/simulator/simctl.d.ts.map +1 -0
  42. package/dist/simulator/simctl.js +96 -0
  43. package/dist/simulator/simctl.js.map +1 -0
  44. package/dist/simulator/types.d.ts +25 -0
  45. package/dist/simulator/types.d.ts.map +1 -0
  46. package/dist/simulator/types.js +2 -0
  47. package/dist/simulator/types.js.map +1 -0
  48. package/dist/tools/flutter-commands.d.ts +128 -0
  49. package/dist/tools/flutter-commands.d.ts.map +1 -0
  50. package/dist/tools/flutter-commands.js +275 -0
  51. package/dist/tools/flutter-commands.js.map +1 -0
  52. package/dist/tools/index.d.ts +3 -0
  53. package/dist/tools/index.d.ts.map +1 -0
  54. package/dist/tools/index.js +499 -0
  55. package/dist/tools/index.js.map +1 -0
  56. package/dist/tools/session.d.ts +38 -0
  57. package/dist/tools/session.d.ts.map +1 -0
  58. package/dist/tools/session.js +40 -0
  59. package/dist/tools/session.js.map +1 -0
  60. package/dist/tools/simulator-ui.d.ts +102 -0
  61. package/dist/tools/simulator-ui.d.ts.map +1 -0
  62. package/dist/tools/simulator-ui.js +117 -0
  63. package/dist/tools/simulator-ui.js.map +1 -0
  64. package/dist/tools/simulator.d.ts +7 -0
  65. package/dist/tools/simulator.d.ts.map +1 -0
  66. package/dist/tools/simulator.js +8 -0
  67. package/dist/tools/simulator.js.map +1 -0
  68. package/dist/transport.d.ts +4 -0
  69. package/dist/transport.d.ts.map +1 -0
  70. package/dist/transport.js +20 -0
  71. package/dist/transport.js.map +1 -0
  72. package/dist/utils/exec.d.ts +29 -0
  73. package/dist/utils/exec.d.ts.map +1 -0
  74. package/dist/utils/exec.js +109 -0
  75. package/dist/utils/exec.js.map +1 -0
  76. package/dist/utils/logger.d.ts +17 -0
  77. package/dist/utils/logger.d.ts.map +1 -0
  78. package/dist/utils/logger.js +60 -0
  79. package/dist/utils/logger.js.map +1 -0
  80. package/package.json +76 -0
@@ -0,0 +1,209 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import { existsSync, statSync } from 'fs';
3
+ import { resolve, join } from 'path';
4
+ import { sessionState } from './state.js';
5
+ import { logger } from '../utils/logger.js';
6
+ import { createSimulator, bootSimulator, shutdownSimulator, deleteSimulator } from '../simulator/simctl.js';
7
+ export class SessionManager {
8
+ allowedPathPrefix;
9
+ maxSessions;
10
+ preBuildScript;
11
+ postBuildScript;
12
+ constructor(allowedPathPrefix = '/Users/', maxSessions = 10) {
13
+ this.allowedPathPrefix = allowedPathPrefix;
14
+ this.maxSessions = maxSessions;
15
+ }
16
+ /**
17
+ * Configure the session manager with security settings.
18
+ *
19
+ * @param allowedPathPrefix - Absolute path prefix for allowed Flutter projects (e.g., "/Users/")
20
+ * @param maxSessions - Optional maximum number of concurrent sessions
21
+ * @param preBuildScript - Optional command to run before flutter build/run
22
+ * @param postBuildScript - Optional command to run after flutter build/run
23
+ *
24
+ * @example
25
+ * sessionManager.configure('/Users/alice/projects', 20, 'git pull', 'echo Done');
26
+ */
27
+ configure(allowedPathPrefix, maxSessions, preBuildScript, postBuildScript) {
28
+ this.allowedPathPrefix = allowedPathPrefix;
29
+ if (maxSessions !== undefined) {
30
+ this.maxSessions = maxSessions;
31
+ }
32
+ this.preBuildScript = preBuildScript;
33
+ this.postBuildScript = postBuildScript;
34
+ logger.info('SessionManager configured', {
35
+ allowedPathPrefix,
36
+ maxSessions: this.maxSessions,
37
+ preBuildScript: preBuildScript || 'none',
38
+ postBuildScript: postBuildScript || 'none',
39
+ });
40
+ }
41
+ /**
42
+ * Create a new development session with an iOS Simulator.
43
+ *
44
+ * Creates a fresh iOS Simulator, boots it, and associates it with the Flutter project directory.
45
+ * Validates that the project path is within allowed directories and contains a valid Flutter project.
46
+ *
47
+ * @param params - Session creation parameters
48
+ * @param params.worktreePath - Absolute path to Flutter project directory (must contain pubspec.yaml)
49
+ * @param params.deviceType - iOS device type to simulate (default: "iPhone 16 Pro")
50
+ *
51
+ * @returns Session information including unique ID and simulator UDID
52
+ *
53
+ * @throws {Error} If session limit is reached
54
+ * @throws {Error} If path is outside allowed prefix
55
+ * @throws {Error} If project directory doesn't exist or isn't valid
56
+ * @throws {Error} If simulator creation or boot fails
57
+ *
58
+ * @example
59
+ * const session = await sessionManager.createSession({
60
+ * worktreePath: '/Users/alice/my-flutter-app',
61
+ * deviceType: 'iPhone 16 Pro'
62
+ * });
63
+ * // Returns: { id: 'uuid...', worktreePath: '...', simulatorUdid: '...', deviceType: '...' }
64
+ */
65
+ async createSession(params) {
66
+ const { worktreePath, deviceType = 'iPhone 16 Pro' } = params;
67
+ logger.info('Creating session', { worktreePath, deviceType });
68
+ // Check session limit
69
+ if (sessionState.size() >= this.maxSessions) {
70
+ throw new Error(`Maximum number of sessions (${String(this.maxSessions)}) reached. ` +
71
+ `End an existing session before creating a new one. ` +
72
+ `Active sessions: ${String(sessionState.size())}`);
73
+ }
74
+ // Validate project path exists and is a directory
75
+ const resolvedPath = resolve(worktreePath);
76
+ // Security: Validate path is within allowed prefix
77
+ if (!resolvedPath.startsWith(this.allowedPathPrefix)) {
78
+ throw new Error(`Access denied: Project path must be under ${this.allowedPathPrefix}. ` +
79
+ `Provided path: ${resolvedPath}`);
80
+ }
81
+ if (!existsSync(resolvedPath)) {
82
+ throw new Error(`Flutter project directory does not exist: ${worktreePath}. ` +
83
+ 'Ensure the path is correct and accessible.');
84
+ }
85
+ const stats = statSync(resolvedPath);
86
+ if (!stats.isDirectory()) {
87
+ throw new Error(`Path is not a directory: ${worktreePath}. ` +
88
+ 'Provide a path to a directory containing a Flutter project (with pubspec.yaml).');
89
+ }
90
+ // Security: Validate it's a Flutter project by checking for pubspec.yaml
91
+ const pubspecPath = join(resolvedPath, 'pubspec.yaml');
92
+ if (!existsSync(pubspecPath)) {
93
+ throw new Error(`Not a valid Flutter project (missing pubspec.yaml): ${resolvedPath}. ` +
94
+ 'The directory must contain a pubspec.yaml file.');
95
+ }
96
+ const sessionId = uuidv4();
97
+ const simulatorUdid = await createSimulator(deviceType);
98
+ logger.debug('Simulator created', { simulatorUdid });
99
+ await bootSimulator(simulatorUdid);
100
+ logger.debug('Simulator booted', { simulatorUdid });
101
+ const session = {
102
+ id: sessionId,
103
+ worktreePath,
104
+ simulatorUdid,
105
+ deviceType,
106
+ createdAt: new Date(),
107
+ };
108
+ sessionState.set(sessionId, session);
109
+ logger.info('Session created', { sessionId, simulatorUdid });
110
+ return {
111
+ id: session.id,
112
+ worktreePath: session.worktreePath,
113
+ simulatorUdid: session.simulatorUdid,
114
+ deviceType: session.deviceType,
115
+ createdAt: session.createdAt.toISOString(),
116
+ };
117
+ }
118
+ async endSession(sessionId) {
119
+ logger.info('Ending session', { sessionId });
120
+ const session = sessionState.get(sessionId);
121
+ if (!session) {
122
+ throw new Error(`Session not found: ${sessionId}`);
123
+ }
124
+ if (session.flutterProcessManager) {
125
+ try {
126
+ await session.flutterProcessManager.cleanup();
127
+ logger.debug('Flutter process cleaned up', { sessionId });
128
+ }
129
+ catch (error) {
130
+ logger.warn('Failed to cleanup Flutter process', {
131
+ sessionId,
132
+ error: String(error),
133
+ });
134
+ }
135
+ }
136
+ try {
137
+ await shutdownSimulator(session.simulatorUdid);
138
+ logger.debug('Simulator shutdown', { simulatorUdid: session.simulatorUdid });
139
+ }
140
+ catch (error) {
141
+ logger.warn('Failed to shutdown simulator', {
142
+ simulatorUdid: session.simulatorUdid,
143
+ error: String(error),
144
+ });
145
+ }
146
+ try {
147
+ await deleteSimulator(session.simulatorUdid);
148
+ logger.debug('Simulator deleted', { simulatorUdid: session.simulatorUdid });
149
+ }
150
+ catch (error) {
151
+ logger.warn('Failed to delete simulator', {
152
+ simulatorUdid: session.simulatorUdid,
153
+ error: String(error),
154
+ });
155
+ }
156
+ sessionState.delete(sessionId);
157
+ logger.info('Session ended', { sessionId });
158
+ }
159
+ /**
160
+ * List all active sessions.
161
+ *
162
+ * @returns Array of session information for all active sessions
163
+ */
164
+ listSessions() {
165
+ return sessionState.list();
166
+ }
167
+ /**
168
+ * Get detailed information about a specific session.
169
+ *
170
+ * @param sessionId - Unique session identifier from createSession()
171
+ * @returns Session object with full state, or undefined if not found
172
+ */
173
+ getSession(sessionId) {
174
+ return sessionState.get(sessionId);
175
+ }
176
+ /**
177
+ * Get the pre-build script command if configured.
178
+ *
179
+ * @returns Pre-build script command, or undefined if not set
180
+ */
181
+ getPreBuildScript() {
182
+ return this.preBuildScript;
183
+ }
184
+ /**
185
+ * Get the post-build script command if configured.
186
+ *
187
+ * @returns Post-build script command, or undefined if not set
188
+ */
189
+ getPostBuildScript() {
190
+ return this.postBuildScript;
191
+ }
192
+ async cleanup() {
193
+ logger.info('Cleaning up all sessions');
194
+ const sessions = sessionState.list();
195
+ for (const session of sessions) {
196
+ try {
197
+ await this.endSession(session.id);
198
+ }
199
+ catch (error) {
200
+ logger.error('Failed to end session during cleanup', {
201
+ sessionId: session.id,
202
+ error: String(error),
203
+ });
204
+ }
205
+ }
206
+ }
207
+ }
208
+ export const sessionManager = new SessionManager();
209
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Session, SessionInfo } from './types.js';
2
+ declare class SessionState {
3
+ private sessions;
4
+ set(sessionId: string, session: Session): void;
5
+ get(sessionId: string): Session | undefined;
6
+ has(sessionId: string): boolean;
7
+ delete(sessionId: string): boolean;
8
+ list(): SessionInfo[];
9
+ clear(): void;
10
+ size(): number;
11
+ }
12
+ export declare const sessionState: SessionState;
13
+ export {};
14
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/session/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElD,cAAM,YAAY;IAChB,OAAO,CAAC,QAAQ,CAAmC;IAEnD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAI9C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAI3C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlC,IAAI,IAAI,WAAW,EAAE;IAqBrB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,MAAM;CAGf;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -0,0 +1,42 @@
1
+ class SessionState {
2
+ sessions = new Map();
3
+ set(sessionId, session) {
4
+ this.sessions.set(sessionId, session);
5
+ }
6
+ get(sessionId) {
7
+ return this.sessions.get(sessionId);
8
+ }
9
+ has(sessionId) {
10
+ return this.sessions.has(sessionId);
11
+ }
12
+ delete(sessionId) {
13
+ return this.sessions.delete(sessionId);
14
+ }
15
+ list() {
16
+ return Array.from(this.sessions.values()).map((session) => {
17
+ const flutterStatus = session.flutterProcessManager?.getStatus();
18
+ return {
19
+ id: session.id,
20
+ worktreePath: session.worktreePath,
21
+ simulatorUdid: session.simulatorUdid,
22
+ deviceType: session.deviceType,
23
+ createdAt: session.createdAt.toISOString(),
24
+ flutterProcess: flutterStatus
25
+ ? {
26
+ pid: flutterStatus.pid,
27
+ status: flutterStatus.status,
28
+ startedAt: flutterStatus.startedAt.toISOString(),
29
+ }
30
+ : undefined,
31
+ };
32
+ });
33
+ }
34
+ clear() {
35
+ this.sessions.clear();
36
+ }
37
+ size() {
38
+ return this.sessions.size;
39
+ }
40
+ }
41
+ export const sessionState = new SessionState();
42
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/session/state.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY;IACR,QAAQ,GAAyB,IAAI,GAAG,EAAE,CAAC;IAEnD,GAAG,CAAC,SAAiB,EAAE,OAAgB;QACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YACxD,MAAM,aAAa,GAAG,OAAO,CAAC,qBAAqB,EAAE,SAAS,EAAE,CAAC;YAEjE,OAAO;gBACL,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;gBAC1C,cAAc,EAAE,aAAa;oBAC3B,CAAC,CAAC;wBACE,GAAG,EAAE,aAAa,CAAC,GAAG;wBACtB,MAAM,EAAE,aAAa,CAAC,MAAM;wBAC5B,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,WAAW,EAAE;qBACjD;oBACH,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { FlutterProcessManager } from '../flutter/process.js';
2
+ export interface Session {
3
+ id: string;
4
+ worktreePath: string;
5
+ simulatorUdid: string;
6
+ deviceType: string;
7
+ createdAt: Date;
8
+ flutterProcessManager?: FlutterProcessManager;
9
+ }
10
+ export interface CreateSessionParams {
11
+ worktreePath: string;
12
+ deviceType?: string;
13
+ }
14
+ export interface SessionInfo {
15
+ id: string;
16
+ worktreePath: string;
17
+ simulatorUdid: string;
18
+ deviceType: string;
19
+ createdAt: string;
20
+ flutterProcess?: {
21
+ pid: number;
22
+ status: string;
23
+ startedAt: string;
24
+ };
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ export interface TapOptions {
2
+ x: number;
3
+ y: number;
4
+ duration?: number;
5
+ }
6
+ export interface SwipeOptions {
7
+ x_start: number;
8
+ y_start: number;
9
+ x_end: number;
10
+ y_end: number;
11
+ duration?: number;
12
+ }
13
+ export interface ScreenshotResult {
14
+ path: string;
15
+ imageData: string;
16
+ format: string;
17
+ }
18
+ /**
19
+ * Tap at coordinates on the simulator screen
20
+ */
21
+ export declare function tap(udid: string, options: TapOptions): Promise<void>;
22
+ /**
23
+ * Input text into the focused field
24
+ */
25
+ export declare function typeText(udid: string, text: string): Promise<void>;
26
+ /**
27
+ * Swipe from one point to another
28
+ */
29
+ export declare function swipe(udid: string, options: SwipeOptions): Promise<void>;
30
+ /**
31
+ * Get accessibility tree for entire screen
32
+ */
33
+ export declare function describeAll(udid: string): Promise<string>;
34
+ /**
35
+ * Get accessibility information at a specific point
36
+ */
37
+ export declare function describePoint(udid: string, x: number, y: number): Promise<string>;
38
+ /**
39
+ * Take a screenshot and save to file
40
+ */
41
+ export declare function screenshot(udid: string): Promise<ScreenshotResult>;
42
+ //# sourceMappingURL=idb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idb.d.ts","sourceRoot":"","sources":["../../src/simulator/idb.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB1E;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBxE;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B9E;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB/D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBvF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgDxE"}
@@ -0,0 +1,127 @@
1
+ import { execFile } from '../utils/exec.js';
2
+ import { logger } from '../utils/logger.js';
3
+ import { readFileSync, mkdtempSync, rmSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { tmpdir } from 'os';
6
+ /**
7
+ * Tap at coordinates on the simulator screen
8
+ */
9
+ export async function tap(udid, options) {
10
+ logger.debug('IDB tap', { udid, ...options });
11
+ const args = ['ui', 'tap', '--udid', udid, String(options.x), String(options.y)];
12
+ if (options.duration) {
13
+ args.push('--duration', String(options.duration));
14
+ }
15
+ const { exitCode, stderr } = await execFile('idb', args, { timeout: 10000 });
16
+ if (exitCode !== 0) {
17
+ throw new Error(`Failed to tap at (${String(options.x)}, ${String(options.y)}) on simulator ${udid}: ${stderr}. ` +
18
+ 'Ensure the simulator is booted and IDB companion is running.');
19
+ }
20
+ logger.info('Tap executed', { udid, ...options });
21
+ }
22
+ /**
23
+ * Input text into the focused field
24
+ */
25
+ export async function typeText(udid, text) {
26
+ logger.debug('IDB text input', { udid, text });
27
+ const { exitCode, stderr } = await execFile('idb', ['ui', 'text', '--udid', udid, text], { timeout: 10000 });
28
+ if (exitCode !== 0) {
29
+ throw new Error(`Failed to type text on simulator ${udid}: ${stderr}. ` +
30
+ 'Ensure a text field is focused and the simulator is responsive.');
31
+ }
32
+ logger.info('Text input executed', { udid, textLength: text.length });
33
+ }
34
+ /**
35
+ * Swipe from one point to another
36
+ */
37
+ export async function swipe(udid, options) {
38
+ logger.debug('IDB swipe', { udid, ...options });
39
+ const args = [
40
+ 'ui',
41
+ 'swipe',
42
+ '--udid',
43
+ udid,
44
+ String(options.x_start),
45
+ String(options.y_start),
46
+ String(options.x_end),
47
+ String(options.y_end),
48
+ ];
49
+ if (options.duration) {
50
+ args.push('--duration', String(options.duration));
51
+ }
52
+ const { exitCode, stderr } = await execFile('idb', args, { timeout: 10000 });
53
+ if (exitCode !== 0) {
54
+ throw new Error(`Failed to swipe from (${String(options.x_start)}, ${String(options.y_start)}) to (${String(options.x_end)}, ${String(options.y_end)}) on simulator ${udid}: ${stderr}. ` +
55
+ 'Ensure the simulator is booted and responsive.');
56
+ }
57
+ logger.info('Swipe executed', { udid, ...options });
58
+ }
59
+ /**
60
+ * Get accessibility tree for entire screen
61
+ */
62
+ export async function describeAll(udid) {
63
+ logger.debug('IDB describe all', { udid });
64
+ const { stdout, stderr, exitCode } = await execFile('idb', ['ui', 'describe-all', '--udid', udid], { timeout: 15000 });
65
+ if (exitCode !== 0) {
66
+ throw new Error(`Failed to get accessibility tree for simulator ${udid}: ${stderr}. ` +
67
+ 'Ensure the simulator is booted with an app running.');
68
+ }
69
+ logger.info('UI described', { udid, outputLength: stdout.length });
70
+ return stdout;
71
+ }
72
+ /**
73
+ * Get accessibility information at a specific point
74
+ */
75
+ export async function describePoint(udid, x, y) {
76
+ logger.debug('IDB describe point', { udid, x, y });
77
+ const { stdout, stderr, exitCode } = await execFile('idb', ['ui', 'describe-point', '--udid', udid, String(x), String(y)], { timeout: 10000 });
78
+ if (exitCode !== 0) {
79
+ throw new Error(`Failed to describe point (${String(x)}, ${String(y)}) on simulator ${udid}: ${stderr}. ` +
80
+ 'Ensure the simulator is booted with an app running.');
81
+ }
82
+ logger.info('Point described', { udid, x, y });
83
+ return stdout;
84
+ }
85
+ /**
86
+ * Take a screenshot and save to file
87
+ */
88
+ export async function screenshot(udid) {
89
+ logger.debug('IDB screenshot', { udid });
90
+ // Create temp file for screenshot
91
+ const tempDir = mkdtempSync(join(tmpdir(), 'mcp-screenshot-'));
92
+ const tempPath = join(tempDir, 'screenshot.png');
93
+ try {
94
+ const { exitCode, stderr } = await execFile('idb', ['screenshot', '--udid', udid, tempPath], { timeout: 15000 });
95
+ if (exitCode !== 0) {
96
+ throw new Error(`Failed to take screenshot on simulator ${udid}: ${stderr}. ` +
97
+ 'Ensure the simulator is booted.');
98
+ }
99
+ // Read screenshot and encode as base64
100
+ const imageBuffer = readFileSync(tempPath);
101
+ const imageData = imageBuffer.toString('base64');
102
+ logger.info('Screenshot captured', {
103
+ udid,
104
+ tempPath,
105
+ sizeBytes: imageBuffer.length
106
+ });
107
+ return {
108
+ path: tempPath,
109
+ imageData,
110
+ format: 'png'
111
+ };
112
+ }
113
+ finally {
114
+ // Clean up temp directory
115
+ try {
116
+ rmSync(tempDir, { recursive: true, force: true });
117
+ logger.debug('Cleaned up screenshot temp directory', { tempDir });
118
+ }
119
+ catch (error) {
120
+ logger.warn('Failed to cleanup screenshot temp directory', {
121
+ tempDir,
122
+ error: String(error)
123
+ });
124
+ }
125
+ }
126
+ }
127
+ //# sourceMappingURL=idb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idb.js","sourceRoot":"","sources":["../../src/simulator/idb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { DeviceType } from './types.js';
2
+ export declare function listDeviceTypes(): Promise<DeviceType[]>;
3
+ export declare function getDeviceTypeIdentifier(deviceName: string): Promise<string>;
4
+ export declare function createSimulator(deviceType: string): Promise<string>;
5
+ export declare function bootSimulator(udid: string): Promise<void>;
6
+ export declare function shutdownSimulator(udid: string): Promise<void>;
7
+ export declare function deleteSimulator(udid: string): Promise<void>;
8
+ export declare function getSimulatorStatus(udid: string): Promise<string>;
9
+ //# sourceMappingURL=simctl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simctl.d.ts","sourceRoot":"","sources":["../../src/simulator/simctl.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAoB,MAAM,YAAY,CAAC;AAE1D,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CA2B7D;AAED,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAejF;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/D;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAanE;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjE;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BtE"}
@@ -0,0 +1,96 @@
1
+ import { execFile } from '../utils/exec.js';
2
+ import { logger } from '../utils/logger.js';
3
+ export async function listDeviceTypes() {
4
+ logger.debug('Listing device types');
5
+ const result = await execFile('xcrun', ['simctl', 'list', 'devicetypes', '-j']);
6
+ if (result.exitCode !== 0) {
7
+ throw new Error(`Failed to list device types: ${result.stderr}`);
8
+ }
9
+ let output;
10
+ try {
11
+ output = JSON.parse(result.stdout);
12
+ }
13
+ catch (error) {
14
+ throw new Error(`Failed to parse simctl output: ${error instanceof Error ? error.message : String(error)}. ` +
15
+ `Output preview: ${result.stdout.substring(0, 200)}`);
16
+ }
17
+ const deviceTypes = output.devicetypes || [];
18
+ const iPhoneTypes = deviceTypes.filter((dt) => dt.name.toLowerCase().includes('iphone'));
19
+ logger.debug('Found device types', { count: iPhoneTypes.length });
20
+ return iPhoneTypes;
21
+ }
22
+ export async function getDeviceTypeIdentifier(deviceName) {
23
+ const deviceTypes = await listDeviceTypes();
24
+ const match = deviceTypes.find((dt) => dt.name.toLowerCase().includes(deviceName.toLowerCase()));
25
+ if (!match) {
26
+ const availableNames = deviceTypes.map((dt) => dt.name).join(', ');
27
+ throw new Error(`Device type not found: ${deviceName}. Available: ${availableNames}`);
28
+ }
29
+ return match.identifier;
30
+ }
31
+ export async function createSimulator(deviceType) {
32
+ logger.info('Creating simulator', { deviceType });
33
+ const deviceTypeId = await getDeviceTypeIdentifier(deviceType);
34
+ const simulatorName = `MCP-${String(Date.now())}`;
35
+ const result = await execFile('xcrun', ['simctl', 'create', simulatorName, deviceTypeId], { timeout: 30000 });
36
+ if (result.exitCode !== 0) {
37
+ throw new Error(`Failed to create simulator "${simulatorName}" with device type "${deviceType}": ${result.stderr}. ` +
38
+ 'This may indicate insufficient disk space or Xcode/CoreSimulator issues. ' +
39
+ 'Try running "xcrun simctl list devicetypes" to verify available device types.');
40
+ }
41
+ const udid = result.stdout.trim();
42
+ logger.info('Simulator created', { udid, name: simulatorName });
43
+ return udid;
44
+ }
45
+ export async function bootSimulator(udid) {
46
+ logger.info('Booting simulator', { udid });
47
+ const result = await execFile('xcrun', ['simctl', 'boot', udid], { timeout: 60000 });
48
+ if (result.exitCode !== 0 && !result.stderr.includes('Unable to boot device in current state: Booted')) {
49
+ throw new Error(`Failed to boot simulator (UDID: ${udid}): ${result.stderr}. ` +
50
+ 'This may indicate the simulator is in an invalid state or Xcode/CoreSimulator is not running properly. ' +
51
+ 'Try running "xcrun simctl list" to check simulator status.');
52
+ }
53
+ logger.info('Simulator booted', { udid });
54
+ }
55
+ export async function shutdownSimulator(udid) {
56
+ logger.info('Shutting down simulator', { udid });
57
+ const result = await execFile('xcrun', ['simctl', 'shutdown', udid]);
58
+ if (result.exitCode !== 0 && !result.stderr.includes('Unable to shutdown device in current state: Shutdown')) {
59
+ logger.warn('Failed to shutdown simulator', {
60
+ udid,
61
+ stderr: result.stderr,
62
+ });
63
+ }
64
+ logger.info('Simulator shutdown', { udid });
65
+ }
66
+ export async function deleteSimulator(udid) {
67
+ logger.info('Deleting simulator', { udid });
68
+ const result = await execFile('xcrun', ['simctl', 'delete', udid]);
69
+ if (result.exitCode !== 0) {
70
+ throw new Error(`Failed to delete simulator: ${result.stderr}`);
71
+ }
72
+ logger.info('Simulator deleted', { udid });
73
+ }
74
+ export async function getSimulatorStatus(udid) {
75
+ const result = await execFile('xcrun', ['simctl', 'list', 'devices', '-j']);
76
+ if (result.exitCode !== 0) {
77
+ throw new Error(`Failed to list devices: ${result.stderr}`);
78
+ }
79
+ let output;
80
+ try {
81
+ output = JSON.parse(result.stdout);
82
+ }
83
+ catch (error) {
84
+ throw new Error(`Failed to parse simctl devices output: ${error instanceof Error ? error.message : String(error)}. ` +
85
+ `Output preview: ${result.stdout.substring(0, 200)}`);
86
+ }
87
+ const devices = output.devices || {};
88
+ for (const runtime of Object.keys(devices)) {
89
+ const device = devices[runtime].find((d) => d.udid === udid);
90
+ if (device) {
91
+ return device.state;
92
+ }
93
+ }
94
+ throw new Error(`Simulator not found: ${udid}`);
95
+ }
96
+ //# sourceMappingURL=simctl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simctl.js","sourceRoot":"","sources":["../../src/simulator/simctl.ts"],"names":[],"mappings":"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"}