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.
- package/CLAUDE.md +74 -0
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/dist/flutter/log-buffer.d.ts +14 -0
- package/dist/flutter/log-buffer.d.ts.map +1 -0
- package/dist/flutter/log-buffer.js +49 -0
- package/dist/flutter/log-buffer.js.map +1 -0
- package/dist/flutter/process.d.ts +30 -0
- package/dist/flutter/process.d.ts.map +1 -0
- package/dist/flutter/process.js +241 -0
- package/dist/flutter/process.js.map +1 -0
- package/dist/flutter/types.d.ts +31 -0
- package/dist/flutter/types.d.ts.map +1 -0
- package/dist/flutter/types.js +2 -0
- package/dist/flutter/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +18 -0
- package/dist/server.js.map +1 -0
- package/dist/session/manager.d.ts +74 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +209 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/state.d.ts +14 -0
- package/dist/session/state.d.ts.map +1 -0
- package/dist/session/state.js +42 -0
- package/dist/session/state.js.map +1 -0
- package/dist/session/types.d.ts +26 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/simulator/idb.d.ts +42 -0
- package/dist/simulator/idb.d.ts.map +1 -0
- package/dist/simulator/idb.js +127 -0
- package/dist/simulator/idb.js.map +1 -0
- package/dist/simulator/simctl.d.ts +9 -0
- package/dist/simulator/simctl.d.ts.map +1 -0
- package/dist/simulator/simctl.js +96 -0
- package/dist/simulator/simctl.js.map +1 -0
- package/dist/simulator/types.d.ts +25 -0
- package/dist/simulator/types.d.ts.map +1 -0
- package/dist/simulator/types.js +2 -0
- package/dist/simulator/types.js.map +1 -0
- package/dist/tools/flutter-commands.d.ts +128 -0
- package/dist/tools/flutter-commands.d.ts.map +1 -0
- package/dist/tools/flutter-commands.js +275 -0
- package/dist/tools/flutter-commands.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +499 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/session.d.ts +38 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +40 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/simulator-ui.d.ts +102 -0
- package/dist/tools/simulator-ui.d.ts.map +1 -0
- package/dist/tools/simulator-ui.js +117 -0
- package/dist/tools/simulator-ui.js.map +1 -0
- package/dist/tools/simulator.d.ts +7 -0
- package/dist/tools/simulator.d.ts.map +1 -0
- package/dist/tools/simulator.js +8 -0
- package/dist/tools/simulator.js.map +1 -0
- package/dist/transport.d.ts +4 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +20 -0
- package/dist/transport.js.map +1 -0
- package/dist/utils/exec.d.ts +29 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +109 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +60 -0
- package/dist/utils/logger.js.map +1 -0
- 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 @@
|
|
|
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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"}
|