@midscene/ios 1.9.2 → 1.9.3-beta-20260608113104.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/dist/es/bin.mjs +64 -8
- package/dist/es/cli.mjs +65 -9
- package/dist/es/index.mjs +64 -8
- package/dist/es/mcp-server.mjs +65 -9
- package/dist/lib/bin.js +64 -8
- package/dist/lib/cli.js +65 -9
- package/dist/lib/index.js +64 -8
- package/dist/lib/mcp-server.js +65 -9
- package/dist/types/index.d.ts +6 -1
- package/package.json +5 -5
- package/static/index.html +1 -1
- package/static/static/js/{index.832f7e19.js → index.04a095cc.js} +15 -15
- package/static/static/js/index.04a095cc.js.map +1 -0
- package/static/static/js/index.832f7e19.js.map +0 -1
- /package/static/static/js/{index.832f7e19.js.LICENSE.txt → index.04a095cc.js.LICENSE.txt} +0 -0
package/dist/es/bin.mjs
CHANGED
|
@@ -200,6 +200,7 @@ const debugIOS = getDebug('webdriver:ios');
|
|
|
200
200
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
201
201
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
202
202
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
203
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
203
204
|
class IOSWebDriverClient extends WebDriverClient {
|
|
204
205
|
async launchApp(bundleId) {
|
|
205
206
|
this.ensureSession();
|
|
@@ -398,14 +399,57 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
398
399
|
return false;
|
|
399
400
|
}
|
|
400
401
|
}
|
|
401
|
-
async
|
|
402
|
+
async pasteText(text) {
|
|
402
403
|
this.ensureSession();
|
|
404
|
+
const cleanText = text.trim();
|
|
405
|
+
if (!cleanText) return;
|
|
403
406
|
try {
|
|
404
|
-
|
|
405
|
-
await this.
|
|
406
|
-
|
|
407
|
+
await this.setPasteboardText(cleanText);
|
|
408
|
+
await this.sendPasteShortcut();
|
|
409
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
412
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async setPasteboardText(text) {
|
|
416
|
+
this.ensureSession();
|
|
417
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
418
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
419
|
+
contentType: 'plaintext'
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
async sendPasteShortcut() {
|
|
423
|
+
this.ensureSession();
|
|
424
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
425
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
426
|
+
keys: [
|
|
427
|
+
{
|
|
428
|
+
key: 'v',
|
|
429
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
async typeText(text, options) {
|
|
435
|
+
this.ensureSession();
|
|
436
|
+
const cleanText = text.trim();
|
|
437
|
+
if (!cleanText) return;
|
|
438
|
+
const chars = Array.from(cleanText);
|
|
439
|
+
const delayMs = options?.delayMs ?? 0;
|
|
440
|
+
try {
|
|
441
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
442
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
443
|
+
value: [
|
|
444
|
+
chars[i]
|
|
445
|
+
]
|
|
446
|
+
});
|
|
447
|
+
if (i < chars.length - 1) await sleep(delayMs);
|
|
448
|
+
}
|
|
449
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
450
|
+
value: chars
|
|
407
451
|
});
|
|
408
|
-
debugIOS(`Typed text: "${text}"`);
|
|
452
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
409
453
|
} catch (error) {
|
|
410
454
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
411
455
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -868,13 +912,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
868
912
|
async typeText(text, options) {
|
|
869
913
|
if (!text) return;
|
|
870
914
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
871
|
-
|
|
915
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
916
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
917
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
872
918
|
try {
|
|
873
919
|
await sleep(200);
|
|
874
|
-
await this.wdaBackend.typeText(text
|
|
920
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
921
|
+
delayMs
|
|
922
|
+
});
|
|
923
|
+
else try {
|
|
924
|
+
await this.wdaBackend.pasteText(text);
|
|
925
|
+
} catch (pasteError) {
|
|
926
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
927
|
+
await this.wdaBackend.typeText(text, {
|
|
928
|
+
delayMs
|
|
929
|
+
});
|
|
930
|
+
}
|
|
875
931
|
await sleep(300);
|
|
876
932
|
} catch (error) {
|
|
877
|
-
debugDevice(`Failed to
|
|
933
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
878
934
|
throw error;
|
|
879
935
|
}
|
|
880
936
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
package/dist/es/cli.mjs
CHANGED
|
@@ -199,6 +199,7 @@ const debugIOS = getDebug('webdriver:ios');
|
|
|
199
199
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
200
200
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
201
201
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
202
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
202
203
|
class IOSWebDriverClient extends WebDriverClient {
|
|
203
204
|
async launchApp(bundleId) {
|
|
204
205
|
this.ensureSession();
|
|
@@ -397,14 +398,57 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
397
398
|
return false;
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
|
-
async
|
|
401
|
+
async pasteText(text) {
|
|
401
402
|
this.ensureSession();
|
|
403
|
+
const cleanText = text.trim();
|
|
404
|
+
if (!cleanText) return;
|
|
402
405
|
try {
|
|
403
|
-
|
|
404
|
-
await this.
|
|
405
|
-
|
|
406
|
+
await this.setPasteboardText(cleanText);
|
|
407
|
+
await this.sendPasteShortcut();
|
|
408
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
411
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async setPasteboardText(text) {
|
|
415
|
+
this.ensureSession();
|
|
416
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
417
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
418
|
+
contentType: 'plaintext'
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
async sendPasteShortcut() {
|
|
422
|
+
this.ensureSession();
|
|
423
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
424
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
425
|
+
keys: [
|
|
426
|
+
{
|
|
427
|
+
key: 'v',
|
|
428
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
429
|
+
}
|
|
430
|
+
]
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async typeText(text, options) {
|
|
434
|
+
this.ensureSession();
|
|
435
|
+
const cleanText = text.trim();
|
|
436
|
+
if (!cleanText) return;
|
|
437
|
+
const chars = Array.from(cleanText);
|
|
438
|
+
const delayMs = options?.delayMs ?? 0;
|
|
439
|
+
try {
|
|
440
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
441
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
442
|
+
value: [
|
|
443
|
+
chars[i]
|
|
444
|
+
]
|
|
445
|
+
});
|
|
446
|
+
if (i < chars.length - 1) await sleep(delayMs);
|
|
447
|
+
}
|
|
448
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
449
|
+
value: chars
|
|
406
450
|
});
|
|
407
|
-
debugIOS(`Typed text: "${text}"`);
|
|
451
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
408
452
|
} catch (error) {
|
|
409
453
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
410
454
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -867,13 +911,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
867
911
|
async typeText(text, options) {
|
|
868
912
|
if (!text) return;
|
|
869
913
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
870
|
-
|
|
914
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
915
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
916
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
871
917
|
try {
|
|
872
918
|
await sleep(200);
|
|
873
|
-
await this.wdaBackend.typeText(text
|
|
919
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
920
|
+
delayMs
|
|
921
|
+
});
|
|
922
|
+
else try {
|
|
923
|
+
await this.wdaBackend.pasteText(text);
|
|
924
|
+
} catch (pasteError) {
|
|
925
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
926
|
+
await this.wdaBackend.typeText(text, {
|
|
927
|
+
delayMs
|
|
928
|
+
});
|
|
929
|
+
}
|
|
874
930
|
await sleep(300);
|
|
875
931
|
} catch (error) {
|
|
876
|
-
debugDevice(`Failed to
|
|
932
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
877
933
|
throw error;
|
|
878
934
|
}
|
|
879
935
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
|
@@ -1453,7 +1509,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
|
|
|
1453
1509
|
const tools = new IOSMidsceneTools();
|
|
1454
1510
|
runToolsCLI(tools, 'midscene-ios', {
|
|
1455
1511
|
stripPrefix: 'ios_',
|
|
1456
|
-
version: "1.9.
|
|
1512
|
+
version: "1.9.3-beta-20260608113104.0",
|
|
1457
1513
|
extraCommands: createReportCliCommands()
|
|
1458
1514
|
}).catch((e)=>{
|
|
1459
1515
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -20,6 +20,7 @@ const debugIOS = getDebug('webdriver:ios');
|
|
|
20
20
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
21
21
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
22
22
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
23
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
23
24
|
class IOSWebDriverClient extends WebDriverClient {
|
|
24
25
|
async launchApp(bundleId) {
|
|
25
26
|
this.ensureSession();
|
|
@@ -218,14 +219,57 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
218
219
|
return false;
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
|
-
async
|
|
222
|
+
async pasteText(text) {
|
|
222
223
|
this.ensureSession();
|
|
224
|
+
const cleanText = text.trim();
|
|
225
|
+
if (!cleanText) return;
|
|
223
226
|
try {
|
|
224
|
-
|
|
225
|
-
await this.
|
|
226
|
-
|
|
227
|
+
await this.setPasteboardText(cleanText);
|
|
228
|
+
await this.sendPasteShortcut();
|
|
229
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
232
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async setPasteboardText(text) {
|
|
236
|
+
this.ensureSession();
|
|
237
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
238
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
239
|
+
contentType: 'plaintext'
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async sendPasteShortcut() {
|
|
243
|
+
this.ensureSession();
|
|
244
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
245
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
246
|
+
keys: [
|
|
247
|
+
{
|
|
248
|
+
key: 'v',
|
|
249
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async typeText(text, options) {
|
|
255
|
+
this.ensureSession();
|
|
256
|
+
const cleanText = text.trim();
|
|
257
|
+
if (!cleanText) return;
|
|
258
|
+
const chars = Array.from(cleanText);
|
|
259
|
+
const delayMs = options?.delayMs ?? 0;
|
|
260
|
+
try {
|
|
261
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
262
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
263
|
+
value: [
|
|
264
|
+
chars[i]
|
|
265
|
+
]
|
|
266
|
+
});
|
|
267
|
+
if (i < chars.length - 1) await sleep(delayMs);
|
|
268
|
+
}
|
|
269
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
270
|
+
value: chars
|
|
227
271
|
});
|
|
228
|
-
debugIOS(`Typed text: "${text}"`);
|
|
272
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
229
273
|
} catch (error) {
|
|
230
274
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
231
275
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -688,13 +732,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
688
732
|
async typeText(text, options) {
|
|
689
733
|
if (!text) return;
|
|
690
734
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
691
|
-
|
|
735
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
736
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
737
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
692
738
|
try {
|
|
693
739
|
await sleep(200);
|
|
694
|
-
await this.wdaBackend.typeText(text
|
|
740
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
741
|
+
delayMs
|
|
742
|
+
});
|
|
743
|
+
else try {
|
|
744
|
+
await this.wdaBackend.pasteText(text);
|
|
745
|
+
} catch (pasteError) {
|
|
746
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
747
|
+
await this.wdaBackend.typeText(text, {
|
|
748
|
+
delayMs
|
|
749
|
+
});
|
|
750
|
+
}
|
|
695
751
|
await sleep(300);
|
|
696
752
|
} catch (error) {
|
|
697
|
-
debugDevice(`Failed to
|
|
753
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
698
754
|
throw error;
|
|
699
755
|
}
|
|
700
756
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
package/dist/es/mcp-server.mjs
CHANGED
|
@@ -199,6 +199,7 @@ const debugIOS = getDebug('webdriver:ios');
|
|
|
199
199
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
200
200
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
201
201
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
202
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
202
203
|
class IOSWebDriverClient extends WebDriverClient {
|
|
203
204
|
async launchApp(bundleId) {
|
|
204
205
|
this.ensureSession();
|
|
@@ -397,14 +398,57 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
397
398
|
return false;
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
|
-
async
|
|
401
|
+
async pasteText(text) {
|
|
401
402
|
this.ensureSession();
|
|
403
|
+
const cleanText = text.trim();
|
|
404
|
+
if (!cleanText) return;
|
|
402
405
|
try {
|
|
403
|
-
|
|
404
|
-
await this.
|
|
405
|
-
|
|
406
|
+
await this.setPasteboardText(cleanText);
|
|
407
|
+
await this.sendPasteShortcut();
|
|
408
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
411
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async setPasteboardText(text) {
|
|
415
|
+
this.ensureSession();
|
|
416
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
417
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
418
|
+
contentType: 'plaintext'
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
async sendPasteShortcut() {
|
|
422
|
+
this.ensureSession();
|
|
423
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
424
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
425
|
+
keys: [
|
|
426
|
+
{
|
|
427
|
+
key: 'v',
|
|
428
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
429
|
+
}
|
|
430
|
+
]
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async typeText(text, options) {
|
|
434
|
+
this.ensureSession();
|
|
435
|
+
const cleanText = text.trim();
|
|
436
|
+
if (!cleanText) return;
|
|
437
|
+
const chars = Array.from(cleanText);
|
|
438
|
+
const delayMs = options?.delayMs ?? 0;
|
|
439
|
+
try {
|
|
440
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
441
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
442
|
+
value: [
|
|
443
|
+
chars[i]
|
|
444
|
+
]
|
|
445
|
+
});
|
|
446
|
+
if (i < chars.length - 1) await sleep(delayMs);
|
|
447
|
+
}
|
|
448
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
449
|
+
value: chars
|
|
406
450
|
});
|
|
407
|
-
debugIOS(`Typed text: "${text}"`);
|
|
451
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
408
452
|
} catch (error) {
|
|
409
453
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
410
454
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -867,13 +911,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
867
911
|
async typeText(text, options) {
|
|
868
912
|
if (!text) return;
|
|
869
913
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
870
|
-
|
|
914
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
915
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
916
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
871
917
|
try {
|
|
872
918
|
await sleep(200);
|
|
873
|
-
await this.wdaBackend.typeText(text
|
|
919
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
920
|
+
delayMs
|
|
921
|
+
});
|
|
922
|
+
else try {
|
|
923
|
+
await this.wdaBackend.pasteText(text);
|
|
924
|
+
} catch (pasteError) {
|
|
925
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
926
|
+
await this.wdaBackend.typeText(text, {
|
|
927
|
+
delayMs
|
|
928
|
+
});
|
|
929
|
+
}
|
|
874
930
|
await sleep(300);
|
|
875
931
|
} catch (error) {
|
|
876
|
-
debugDevice(`Failed to
|
|
932
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
877
933
|
throw error;
|
|
878
934
|
}
|
|
879
935
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
|
@@ -1457,7 +1513,7 @@ class IOSMCPServer extends BaseMCPServer {
|
|
|
1457
1513
|
constructor(toolsManager){
|
|
1458
1514
|
super({
|
|
1459
1515
|
name: '@midscene/ios-mcp',
|
|
1460
|
-
version: "1.9.
|
|
1516
|
+
version: "1.9.3-beta-20260608113104.0",
|
|
1461
1517
|
description: 'Control the iOS device using natural language commands'
|
|
1462
1518
|
}, toolsManager);
|
|
1463
1519
|
}
|
package/dist/lib/bin.js
CHANGED
|
@@ -225,6 +225,7 @@ const debugIOS = (0, logger_namespaceObject.getDebug)('webdriver:ios');
|
|
|
225
225
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
226
226
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
227
227
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
228
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
228
229
|
class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
229
230
|
async launchApp(bundleId) {
|
|
230
231
|
this.ensureSession();
|
|
@@ -423,14 +424,57 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
423
424
|
return false;
|
|
424
425
|
}
|
|
425
426
|
}
|
|
426
|
-
async
|
|
427
|
+
async pasteText(text) {
|
|
427
428
|
this.ensureSession();
|
|
429
|
+
const cleanText = text.trim();
|
|
430
|
+
if (!cleanText) return;
|
|
428
431
|
try {
|
|
429
|
-
|
|
430
|
-
await this.
|
|
431
|
-
|
|
432
|
+
await this.setPasteboardText(cleanText);
|
|
433
|
+
await this.sendPasteShortcut();
|
|
434
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
437
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async setPasteboardText(text) {
|
|
441
|
+
this.ensureSession();
|
|
442
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
443
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
444
|
+
contentType: 'plaintext'
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
async sendPasteShortcut() {
|
|
448
|
+
this.ensureSession();
|
|
449
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
450
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
451
|
+
keys: [
|
|
452
|
+
{
|
|
453
|
+
key: 'v',
|
|
454
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
455
|
+
}
|
|
456
|
+
]
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async typeText(text, options) {
|
|
460
|
+
this.ensureSession();
|
|
461
|
+
const cleanText = text.trim();
|
|
462
|
+
if (!cleanText) return;
|
|
463
|
+
const chars = Array.from(cleanText);
|
|
464
|
+
const delayMs = options?.delayMs ?? 0;
|
|
465
|
+
try {
|
|
466
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
467
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
468
|
+
value: [
|
|
469
|
+
chars[i]
|
|
470
|
+
]
|
|
471
|
+
});
|
|
472
|
+
if (i < chars.length - 1) await (0, core_utils_namespaceObject.sleep)(delayMs);
|
|
473
|
+
}
|
|
474
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
475
|
+
value: chars
|
|
432
476
|
});
|
|
433
|
-
debugIOS(`Typed text: "${text}"`);
|
|
477
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
434
478
|
} catch (error) {
|
|
435
479
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
436
480
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -893,13 +937,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
893
937
|
async typeText(text, options) {
|
|
894
938
|
if (!text) return;
|
|
895
939
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
896
|
-
|
|
940
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
941
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
942
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
897
943
|
try {
|
|
898
944
|
await (0, core_utils_namespaceObject.sleep)(200);
|
|
899
|
-
await this.wdaBackend.typeText(text
|
|
945
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
946
|
+
delayMs
|
|
947
|
+
});
|
|
948
|
+
else try {
|
|
949
|
+
await this.wdaBackend.pasteText(text);
|
|
950
|
+
} catch (pasteError) {
|
|
951
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
952
|
+
await this.wdaBackend.typeText(text, {
|
|
953
|
+
delayMs
|
|
954
|
+
});
|
|
955
|
+
}
|
|
900
956
|
await (0, core_utils_namespaceObject.sleep)(300);
|
|
901
957
|
} catch (error) {
|
|
902
|
-
debugDevice(`Failed to
|
|
958
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
903
959
|
throw error;
|
|
904
960
|
}
|
|
905
961
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
package/dist/lib/cli.js
CHANGED
|
@@ -223,6 +223,7 @@ const debugIOS = (0, logger_namespaceObject.getDebug)('webdriver:ios');
|
|
|
223
223
|
const WDA_MJPEG_SCREENSHOT_QUALITY = 50;
|
|
224
224
|
const WDA_MJPEG_FRAMERATE = 30;
|
|
225
225
|
const WDA_MJPEG_SCALING_FACTOR = 50;
|
|
226
|
+
const XCUI_KEY_MODIFIER_COMMAND = 16;
|
|
226
227
|
class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
227
228
|
async launchApp(bundleId) {
|
|
228
229
|
this.ensureSession();
|
|
@@ -421,14 +422,57 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
421
422
|
return false;
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
|
-
async
|
|
425
|
+
async pasteText(text) {
|
|
425
426
|
this.ensureSession();
|
|
427
|
+
const cleanText = text.trim();
|
|
428
|
+
if (!cleanText) return;
|
|
426
429
|
try {
|
|
427
|
-
|
|
428
|
-
await this.
|
|
429
|
-
|
|
430
|
+
await this.setPasteboardText(cleanText);
|
|
431
|
+
await this.sendPasteShortcut();
|
|
432
|
+
debugIOS(`Pasted text: "${text}"`);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
debugIOS(`Failed to paste text "${text}": ${error}`);
|
|
435
|
+
throw new Error(`Failed to paste text: ${error}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async setPasteboardText(text) {
|
|
439
|
+
this.ensureSession();
|
|
440
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/setPasteboard`, {
|
|
441
|
+
content: Buffer.from(text, 'utf8').toString('base64'),
|
|
442
|
+
contentType: 'plaintext'
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
async sendPasteShortcut() {
|
|
446
|
+
this.ensureSession();
|
|
447
|
+
const elementId = await this.getActiveElement() ?? '0';
|
|
448
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/element/${elementId}/keyboardInput`, {
|
|
449
|
+
keys: [
|
|
450
|
+
{
|
|
451
|
+
key: 'v',
|
|
452
|
+
modifierFlags: XCUI_KEY_MODIFIER_COMMAND
|
|
453
|
+
}
|
|
454
|
+
]
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
async typeText(text, options) {
|
|
458
|
+
this.ensureSession();
|
|
459
|
+
const cleanText = text.trim();
|
|
460
|
+
if (!cleanText) return;
|
|
461
|
+
const chars = Array.from(cleanText);
|
|
462
|
+
const delayMs = options?.delayMs ?? 0;
|
|
463
|
+
try {
|
|
464
|
+
if (delayMs > 0) for(let i = 0; i < chars.length; i++){
|
|
465
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
466
|
+
value: [
|
|
467
|
+
chars[i]
|
|
468
|
+
]
|
|
469
|
+
});
|
|
470
|
+
if (i < chars.length - 1) await (0, core_utils_namespaceObject.sleep)(delayMs);
|
|
471
|
+
}
|
|
472
|
+
else await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
|
|
473
|
+
value: chars
|
|
430
474
|
});
|
|
431
|
-
debugIOS(`Typed text: "${text}"`);
|
|
475
|
+
debugIOS(`Typed text: "${text}" (delayMs=${delayMs})`);
|
|
432
476
|
} catch (error) {
|
|
433
477
|
debugIOS(`Failed to type text "${text}": ${error}`);
|
|
434
478
|
throw new Error(`Failed to type text: ${error}`);
|
|
@@ -891,13 +935,25 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
891
935
|
async typeText(text, options) {
|
|
892
936
|
if (!text) return;
|
|
893
937
|
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
894
|
-
|
|
938
|
+
const delayMs = options?.keyboardTypeDelay ?? this.options?.keyboardTypeDelay ?? 80;
|
|
939
|
+
const keyboardInputStrategy = options?.keyboardInputStrategy ?? this.options?.keyboardInputStrategy ?? 'paste';
|
|
940
|
+
debugDevice(`Input text: "${text}" (strategy=${keyboardInputStrategy}, delayMs=${delayMs})`);
|
|
895
941
|
try {
|
|
896
942
|
await (0, core_utils_namespaceObject.sleep)(200);
|
|
897
|
-
await this.wdaBackend.typeText(text
|
|
943
|
+
if ('type' === keyboardInputStrategy) await this.wdaBackend.typeText(text, {
|
|
944
|
+
delayMs
|
|
945
|
+
});
|
|
946
|
+
else try {
|
|
947
|
+
await this.wdaBackend.pasteText(text);
|
|
948
|
+
} catch (pasteError) {
|
|
949
|
+
debugDevice(`Failed to paste text with WDA, falling back to typing: ${pasteError}`);
|
|
950
|
+
await this.wdaBackend.typeText(text, {
|
|
951
|
+
delayMs
|
|
952
|
+
});
|
|
953
|
+
}
|
|
898
954
|
await (0, core_utils_namespaceObject.sleep)(300);
|
|
899
955
|
} catch (error) {
|
|
900
|
-
debugDevice(`Failed to
|
|
956
|
+
debugDevice(`Failed to input text with WDA: ${error}`);
|
|
901
957
|
throw error;
|
|
902
958
|
}
|
|
903
959
|
if (shouldAutoDismissKeyboard) await this.hideKeyboard();
|
|
@@ -1477,7 +1533,7 @@ class IOSMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools {
|
|
|
1477
1533
|
const tools = new IOSMidsceneTools();
|
|
1478
1534
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-ios', {
|
|
1479
1535
|
stripPrefix: 'ios_',
|
|
1480
|
-
version: "1.9.
|
|
1536
|
+
version: "1.9.3-beta-20260608113104.0",
|
|
1481
1537
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
1482
1538
|
}).catch((e)=>{
|
|
1483
1539
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|