mobile-debug-mcp 0.12.4 → 0.12.5
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/dist/ios/manage.js +13 -2
- package/dist/ios/utils.js +49 -0
- package/package.json +1 -1
- package/src/ios/manage.ts +8 -2
- package/src/ios/utils.ts +47 -0
- package/src/types.ts +1 -0
package/dist/ios/manage.js
CHANGED
|
@@ -291,15 +291,26 @@ export class iOSManage {
|
|
|
291
291
|
}
|
|
292
292
|
async startApp(bundleId, deviceId = "booted") {
|
|
293
293
|
validateBundleId(bundleId);
|
|
294
|
+
// Prepare instrumentation object upfront so it can be returned to callers
|
|
295
|
+
const instrumentation = { ts: new Date().toISOString(), action: 'startApp', cmd: 'xcrun', args: ['simctl', 'launch', deviceId, bundleId], cwd: process.cwd(), env: { PATH: process.env.PATH, XCRUN_PATH: process.env.XCRUN_PATH } };
|
|
294
296
|
try {
|
|
297
|
+
// Instrumentation: persist and emit to stderr for server logs
|
|
298
|
+
try {
|
|
299
|
+
await fs.appendFile('/tmp/mcp_startapp_instrument.log', JSON.stringify(instrumentation) + '\n');
|
|
300
|
+
}
|
|
301
|
+
catch (e) { }
|
|
302
|
+
try {
|
|
303
|
+
console.error('MCP-STARTAPP-EXEC', JSON.stringify(instrumentation));
|
|
304
|
+
}
|
|
305
|
+
catch (e) { }
|
|
295
306
|
const result = await execCommand(['simctl', 'launch', deviceId, bundleId], deviceId);
|
|
296
307
|
const device = await getIOSDeviceMetadata(deviceId);
|
|
297
|
-
return { device, appStarted: !!result.output, launchTimeMs: 1000 };
|
|
308
|
+
return { device, appStarted: !!result.output, launchTimeMs: 1000, instrumentation };
|
|
298
309
|
}
|
|
299
310
|
catch (e) {
|
|
300
311
|
const diag = execCommandWithDiagnostics(['simctl', 'launch', deviceId, bundleId], deviceId);
|
|
301
312
|
const device = await getIOSDeviceMetadata(deviceId);
|
|
302
|
-
return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag };
|
|
313
|
+
return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag, instrumentation };
|
|
303
314
|
}
|
|
304
315
|
}
|
|
305
316
|
async terminateApp(bundleId, deviceId = "booted") {
|
package/dist/ios/utils.js
CHANGED
|
@@ -96,7 +96,40 @@ export function validateBundleId(bundleId) {
|
|
|
96
96
|
}
|
|
97
97
|
export function execCommand(args, deviceId = "booted") {
|
|
98
98
|
return new Promise((resolve, reject) => {
|
|
99
|
+
// Instrumentation: append a JSON line with timestamp, command, args, cwd and selected env vars
|
|
100
|
+
try {
|
|
101
|
+
const mcpEnv = {};
|
|
102
|
+
for (const k of Object.keys(process.env || {})) {
|
|
103
|
+
if (k.startsWith('MCP_'))
|
|
104
|
+
mcpEnv[k] = process.env[k];
|
|
105
|
+
}
|
|
106
|
+
const instrument = {
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
command: getXcrunCmd(),
|
|
109
|
+
args,
|
|
110
|
+
cwd: process.cwd(),
|
|
111
|
+
env: {
|
|
112
|
+
PATH: process.env.PATH,
|
|
113
|
+
XCRUN_PATH: process.env.XCRUN_PATH,
|
|
114
|
+
...mcpEnv
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
try {
|
|
118
|
+
require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(instrument) + '\n');
|
|
119
|
+
}
|
|
120
|
+
catch (e) { }
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
// swallow instrumentation errors to avoid changing behavior
|
|
124
|
+
}
|
|
99
125
|
// Use spawn for better stream control and consistency with Android implementation
|
|
126
|
+
// Instrument: emit a JSON line to stderr so the MCP server stderr/stdout capture can record the exact command and env
|
|
127
|
+
try {
|
|
128
|
+
const instLine = JSON.stringify({ ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd(), PATH: process.env.PATH });
|
|
129
|
+
// Use stderr so it appears in server logs reliably
|
|
130
|
+
console.error('MCP-INSTRUMENT-EXEC', instLine);
|
|
131
|
+
}
|
|
132
|
+
catch (e) { }
|
|
100
133
|
const child = spawn(getXcrunCmd(), args);
|
|
101
134
|
let stdout = '';
|
|
102
135
|
let stderr = '';
|
|
@@ -110,6 +143,17 @@ export function execCommand(args, deviceId = "booted") {
|
|
|
110
143
|
stderr += data.toString();
|
|
111
144
|
});
|
|
112
145
|
}
|
|
146
|
+
// Additional instrumentation: write pid and env snapshot when child starts
|
|
147
|
+
try {
|
|
148
|
+
const pidInfo = { ts: new Date().toISOString(), childPid: (child.pid || null), invoked: getXcrunCmd(), args };
|
|
149
|
+
try {
|
|
150
|
+
require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(pidInfo) + '\n');
|
|
151
|
+
}
|
|
152
|
+
catch (e) { }
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
113
157
|
const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000; // env (ms) or default 30s
|
|
114
158
|
const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000; // env (ms) or default 60s
|
|
115
159
|
const timeoutMs = args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT; // choose appropriate timeout
|
|
@@ -133,6 +177,11 @@ export function execCommand(args, deviceId = "booted") {
|
|
|
133
177
|
});
|
|
134
178
|
}
|
|
135
179
|
export function execCommandWithDiagnostics(args, deviceId = "booted") {
|
|
180
|
+
try {
|
|
181
|
+
const syncInst = { ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd() };
|
|
182
|
+
require('fs').appendFileSync('/tmp/mcp_exec_instrument_sync.log', JSON.stringify(syncInst) + '\n');
|
|
183
|
+
}
|
|
184
|
+
catch (e) { }
|
|
136
185
|
// Run synchronously to capture stdout/stderr and exitCode reliably for diagnostics
|
|
137
186
|
const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000;
|
|
138
187
|
const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000;
|
package/package.json
CHANGED
package/src/ios/manage.ts
CHANGED
|
@@ -298,14 +298,20 @@ export class iOSManage {
|
|
|
298
298
|
|
|
299
299
|
async startApp(bundleId: string, deviceId: string = "booted"): Promise<StartAppResponse> {
|
|
300
300
|
validateBundleId(bundleId)
|
|
301
|
+
// Prepare instrumentation object upfront so it can be returned to callers
|
|
302
|
+
const instrumentation = { ts: new Date().toISOString(), action: 'startApp', cmd: 'xcrun', args: ['simctl','launch', deviceId, bundleId], cwd: process.cwd(), env: { PATH: process.env.PATH, XCRUN_PATH: process.env.XCRUN_PATH } }
|
|
301
303
|
try {
|
|
304
|
+
// Instrumentation: persist and emit to stderr for server logs
|
|
305
|
+
try { await fs.appendFile('/tmp/mcp_startapp_instrument.log', JSON.stringify(instrumentation) + '\n') } catch (e) {}
|
|
306
|
+
try { console.error('MCP-STARTAPP-EXEC', JSON.stringify(instrumentation)) } catch (e) {}
|
|
307
|
+
|
|
302
308
|
const result = await execCommand(['simctl', 'launch', deviceId, bundleId], deviceId)
|
|
303
309
|
const device = await getIOSDeviceMetadata(deviceId)
|
|
304
|
-
return { device, appStarted: !!result.output, launchTimeMs: 1000 }
|
|
310
|
+
return { device, appStarted: !!result.output, launchTimeMs: 1000, instrumentation }
|
|
305
311
|
} catch (e:any) {
|
|
306
312
|
const diag = execCommandWithDiagnostics(['simctl', 'launch', deviceId, bundleId], deviceId)
|
|
307
313
|
const device = await getIOSDeviceMetadata(deviceId)
|
|
308
|
-
return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag } as any
|
|
314
|
+
return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag, instrumentation } as any
|
|
309
315
|
}
|
|
310
316
|
}
|
|
311
317
|
|
package/src/ios/utils.ts
CHANGED
|
@@ -88,7 +88,41 @@ export function validateBundleId(bundleId: string) {
|
|
|
88
88
|
|
|
89
89
|
export function execCommand(args: string[], deviceId: string = "booted"): Promise<IOSResult> {
|
|
90
90
|
return new Promise((resolve, reject) => {
|
|
91
|
+
// Instrumentation: append a JSON line with timestamp, command, args, cwd and selected env vars
|
|
92
|
+
try {
|
|
93
|
+
const mcpEnv: Record<string,string|undefined> = {}
|
|
94
|
+
for (const k of Object.keys(process.env || {})) {
|
|
95
|
+
if (k.startsWith('MCP_')) mcpEnv[k] = process.env[k]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const instrument = {
|
|
99
|
+
timestamp: new Date().toISOString(),
|
|
100
|
+
command: getXcrunCmd(),
|
|
101
|
+
args,
|
|
102
|
+
cwd: process.cwd(),
|
|
103
|
+
env: {
|
|
104
|
+
PATH: process.env.PATH,
|
|
105
|
+
XCRUN_PATH: process.env.XCRUN_PATH,
|
|
106
|
+
...mcpEnv
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(instrument) + '\n')
|
|
112
|
+
} catch (e) {}
|
|
113
|
+
|
|
114
|
+
} catch (e) {
|
|
115
|
+
// swallow instrumentation errors to avoid changing behavior
|
|
116
|
+
}
|
|
117
|
+
|
|
91
118
|
// Use spawn for better stream control and consistency with Android implementation
|
|
119
|
+
// Instrument: emit a JSON line to stderr so the MCP server stderr/stdout capture can record the exact command and env
|
|
120
|
+
try {
|
|
121
|
+
const instLine = JSON.stringify({ ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd(), PATH: process.env.PATH })
|
|
122
|
+
// Use stderr so it appears in server logs reliably
|
|
123
|
+
console.error('MCP-INSTRUMENT-EXEC', instLine)
|
|
124
|
+
} catch (e) {}
|
|
125
|
+
|
|
92
126
|
const child = spawn(getXcrunCmd(), args)
|
|
93
127
|
|
|
94
128
|
let stdout = ''
|
|
@@ -106,6 +140,14 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
|
|
|
106
140
|
})
|
|
107
141
|
}
|
|
108
142
|
|
|
143
|
+
// Additional instrumentation: write pid and env snapshot when child starts
|
|
144
|
+
try {
|
|
145
|
+
const pidInfo = { ts: new Date().toISOString(), childPid: (child.pid || null), invoked: getXcrunCmd(), args }
|
|
146
|
+
try { require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(pidInfo) + '\n') } catch (e) {}
|
|
147
|
+
} catch (e) {
|
|
148
|
+
// ignore
|
|
149
|
+
}
|
|
150
|
+
|
|
109
151
|
const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000 // env (ms) or default 30s
|
|
110
152
|
const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000 // env (ms) or default 60s
|
|
111
153
|
const timeoutMs = args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT // choose appropriate timeout
|
|
@@ -131,6 +173,11 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
|
|
|
131
173
|
}
|
|
132
174
|
|
|
133
175
|
export function execCommandWithDiagnostics(args: string[], deviceId: string = "booted") {
|
|
176
|
+
try {
|
|
177
|
+
const syncInst = { ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd() }
|
|
178
|
+
require('fs').appendFileSync('/tmp/mcp_exec_instrument_sync.log', JSON.stringify(syncInst) + '\n')
|
|
179
|
+
} catch (e) {}
|
|
180
|
+
|
|
134
181
|
// Run synchronously to capture stdout/stderr and exitCode reliably for diagnostics
|
|
135
182
|
const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000
|
|
136
183
|
const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000
|