@midscene/ios 1.8.0 → 1.8.1
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 +120 -131
- package/dist/es/cli.mjs +121 -132
- package/dist/es/index.mjs +120 -131
- package/dist/es/mcp-server.mjs +121 -132
- package/dist/lib/bin.js +118 -129
- package/dist/lib/cli.js +119 -130
- package/dist/lib/index.js +118 -129
- package/dist/lib/mcp-server.js +119 -130
- package/dist/types/index.d.ts +11 -7
- package/dist/types/mcp-server.d.ts +11 -7
- package/package.json +5 -5
- package/static/index.html +1 -1
- package/static/static/css/{index.63b028da.css → index.26c9c911.css} +2 -2
- package/static/static/css/{index.63b028da.css.map → index.26c9c911.css.map} +1 -1
- package/static/static/js/{889.c8e2e995.js → 596.5426be9e.js} +27 -27
- package/static/static/js/596.5426be9e.js.map +1 -0
- package/static/static/js/index.acaa5ec1.js +914 -0
- package/static/static/js/index.acaa5ec1.js.map +1 -0
- package/static/static/js/889.c8e2e995.js.map +0 -1
- package/static/static/js/index.7d3d953d.js +0 -914
- package/static/static/js/index.7d3d953d.js.map +0 -1
- /package/static/static/js/{889.c8e2e995.js.LICENSE.txt → 596.5426be9e.js.LICENSE.txt} +0 -0
- /package/static/static/js/{index.7d3d953d.js.LICENSE.txt → index.acaa5ec1.js.LICENSE.txt} +0 -0
package/dist/es/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import node_assert from "node:assert";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { z } from "@midscene/core";
|
|
3
|
+
import { createDefaultMobileActions, defineAction } from "@midscene/core/device";
|
|
4
4
|
import { sleep } from "@midscene/core/utils";
|
|
5
5
|
import { DEFAULT_WDA_PORT, PLAYGROUND_SERVER_PORT } from "@midscene/shared/constants";
|
|
6
6
|
import { createImgBase64ByFormat } from "@midscene/shared/img";
|
|
@@ -470,16 +470,6 @@ function _define_property(obj, key, value) {
|
|
|
470
470
|
return obj;
|
|
471
471
|
}
|
|
472
472
|
const debugDevice = getDebug('ios:device');
|
|
473
|
-
const iosInputParamSchema = z.object({
|
|
474
|
-
value: z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
|
|
475
|
-
autoDismissKeyboard: z.boolean().optional().describe('Whether to dismiss the keyboard after input. Defaults to true if not specified. Set to false to keep the keyboard visible after input.'),
|
|
476
|
-
mode: z.preprocess((val)=>'append' === val ? 'typeOnly' : val, z["enum"]([
|
|
477
|
-
'replace',
|
|
478
|
-
'clear',
|
|
479
|
-
'typeOnly'
|
|
480
|
-
]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "typeOnly" - type the value directly without clearing the field first; "clear" - clear the field without inputting new text.')),
|
|
481
|
-
locate: getMidsceneLocationSchema().describe('The input field to be filled').optional()
|
|
482
|
-
});
|
|
483
473
|
const WDA_HTTP_METHODS = [
|
|
484
474
|
'GET',
|
|
485
475
|
'POST',
|
|
@@ -488,96 +478,39 @@ const WDA_HTTP_METHODS = [
|
|
|
488
478
|
];
|
|
489
479
|
const DEFAULT_WDA_MJPEG_PORT = 9100;
|
|
490
480
|
class IOSDevice {
|
|
481
|
+
async tapPoint(point) {
|
|
482
|
+
debugDevice(`tap at coordinates (${point.x}, ${point.y})`);
|
|
483
|
+
await this.wdaBackend.tap(Math.round(point.x), Math.round(point.y));
|
|
484
|
+
}
|
|
485
|
+
async doubleTapPoint(point) {
|
|
486
|
+
await this.wdaBackend.doubleTap(Math.round(point.x), Math.round(point.y));
|
|
487
|
+
}
|
|
488
|
+
async longPressPoint(point, duration = 1000) {
|
|
489
|
+
await this.wdaBackend.longPress(Math.round(point.x), Math.round(point.y), duration);
|
|
490
|
+
}
|
|
491
|
+
async swipePoint(start, end, duration = 500) {
|
|
492
|
+
await this.wdaBackend.swipe(Math.round(start.x), Math.round(start.y), Math.round(end.x), Math.round(end.y), duration);
|
|
493
|
+
}
|
|
494
|
+
async clearInputAt(point) {
|
|
495
|
+
if (point) {
|
|
496
|
+
await this.tapPoint(point);
|
|
497
|
+
await sleep(100);
|
|
498
|
+
}
|
|
499
|
+
debugDevice('Attempting to clear input with WebDriver Clear API');
|
|
500
|
+
const cleared = await this.wdaBackend.clearActiveElement();
|
|
501
|
+
cleared ? debugDevice('Successfully cleared input with WebDriver Clear API') : debugDevice('WebDriver Clear API returned false (no active element or clear failed)');
|
|
502
|
+
}
|
|
491
503
|
actionSpace() {
|
|
504
|
+
const mobileActionContext = {
|
|
505
|
+
input: this.inputPrimitives,
|
|
506
|
+
size: ()=>this.size(),
|
|
507
|
+
sleep: async (timeMs)=>{
|
|
508
|
+
await sleep(timeMs);
|
|
509
|
+
},
|
|
510
|
+
getDefaultAutoDismissKeyboard: ()=>this.options?.autoDismissKeyboard
|
|
511
|
+
};
|
|
492
512
|
const defaultActions = [
|
|
493
|
-
|
|
494
|
-
const element = param.locate;
|
|
495
|
-
node_assert(element, 'Element not found, cannot tap');
|
|
496
|
-
await this.mouseClick(element.center[0], element.center[1]);
|
|
497
|
-
}),
|
|
498
|
-
defineActionDoubleClick(async (param)=>{
|
|
499
|
-
const element = param.locate;
|
|
500
|
-
node_assert(element, 'Element not found, cannot double click');
|
|
501
|
-
await this.doubleTap(element.center[0], element.center[1]);
|
|
502
|
-
}),
|
|
503
|
-
defineAction({
|
|
504
|
-
name: 'Input',
|
|
505
|
-
description: 'Input text into the input field',
|
|
506
|
-
interfaceAlias: 'aiInput',
|
|
507
|
-
paramSchema: iosInputParamSchema,
|
|
508
|
-
sample: {
|
|
509
|
-
value: 'test@example.com',
|
|
510
|
-
locate: {
|
|
511
|
-
prompt: 'the email input field'
|
|
512
|
-
}
|
|
513
|
-
},
|
|
514
|
-
call: async (param)=>{
|
|
515
|
-
const element = param.locate;
|
|
516
|
-
if ('typeOnly' !== param.mode) await this.clearInput(element);
|
|
517
|
-
if ('clear' === param.mode) return;
|
|
518
|
-
if (!param || !param.value) return;
|
|
519
|
-
const autoDismissKeyboard = param.autoDismissKeyboard ?? this.options?.autoDismissKeyboard;
|
|
520
|
-
await this.typeText(param.value, {
|
|
521
|
-
autoDismissKeyboard
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}),
|
|
525
|
-
defineActionScroll(async (param)=>{
|
|
526
|
-
const element = param.locate;
|
|
527
|
-
const startingPoint = element ? {
|
|
528
|
-
left: element.center[0],
|
|
529
|
-
top: element.center[1]
|
|
530
|
-
} : void 0;
|
|
531
|
-
const scrollToEventName = param?.scrollType;
|
|
532
|
-
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
533
|
-
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
534
|
-
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
535
|
-
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
536
|
-
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
537
|
-
else {
|
|
538
|
-
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
|
|
539
|
-
else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
|
|
540
|
-
else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
|
|
541
|
-
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
542
|
-
else await this.scrollDown(param?.distance || void 0, startingPoint);
|
|
543
|
-
await sleep(500);
|
|
544
|
-
}
|
|
545
|
-
}),
|
|
546
|
-
defineActionDragAndDrop(async (param)=>{
|
|
547
|
-
const from = param.from;
|
|
548
|
-
const to = param.to;
|
|
549
|
-
node_assert(from, 'missing "from" param for drag and drop');
|
|
550
|
-
node_assert(to, 'missing "to" param for drag and drop');
|
|
551
|
-
await this.swipe(from.center[0], from.center[1], to.center[0], to.center[1], 1000);
|
|
552
|
-
}),
|
|
553
|
-
defineActionSwipe(async (param)=>{
|
|
554
|
-
const { startPoint, endPoint, duration, repeatCount } = normalizeMobileSwipeParam(param, await this.size());
|
|
555
|
-
for(let i = 0; i < repeatCount; i++)await this.swipe(startPoint.x, startPoint.y, endPoint.x, endPoint.y, duration);
|
|
556
|
-
}),
|
|
557
|
-
defineActionKeyboardPress(async (param)=>{
|
|
558
|
-
await this.pressKey(param.keyName);
|
|
559
|
-
}),
|
|
560
|
-
defineActionCursorMove(async (param)=>{
|
|
561
|
-
const arrowKey = 'left' === param.direction ? 'ArrowLeft' : 'ArrowRight';
|
|
562
|
-
const times = param.times ?? 1;
|
|
563
|
-
for(let i = 0; i < times; i++){
|
|
564
|
-
await this.pressKey(arrowKey);
|
|
565
|
-
await sleep(100);
|
|
566
|
-
}
|
|
567
|
-
}),
|
|
568
|
-
defineActionLongPress(async (param)=>{
|
|
569
|
-
const element = param.locate;
|
|
570
|
-
node_assert(element, 'LongPress requires an element to be located');
|
|
571
|
-
const [x, y] = element.center;
|
|
572
|
-
await this.longPress(x, y, param?.duration);
|
|
573
|
-
}),
|
|
574
|
-
defineActionPinch(async (param)=>{
|
|
575
|
-
const { centerX, centerY, startDistance, endDistance, duration } = normalizePinchParam(param, await this.size());
|
|
576
|
-
await this.wdaBackend.pinch(centerX, centerY, startDistance, endDistance, duration);
|
|
577
|
-
}),
|
|
578
|
-
defineActionClearInput(async (param)=>{
|
|
579
|
-
await this.clearInput(param.locate);
|
|
580
|
-
})
|
|
513
|
+
...createDefaultMobileActions(mobileActionContext)
|
|
581
514
|
];
|
|
582
515
|
const platformSpecificActions = Object.values(createPlatformActions(this));
|
|
583
516
|
const customActions = this.customActions || [];
|
|
@@ -587,6 +520,27 @@ class IOSDevice {
|
|
|
587
520
|
...customActions
|
|
588
521
|
];
|
|
589
522
|
}
|
|
523
|
+
async performActionScroll(param) {
|
|
524
|
+
const element = param.locate;
|
|
525
|
+
const startingPoint = element ? {
|
|
526
|
+
left: element.center[0],
|
|
527
|
+
top: element.center[1]
|
|
528
|
+
} : void 0;
|
|
529
|
+
const scrollToEventName = param?.scrollType;
|
|
530
|
+
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
531
|
+
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
532
|
+
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
533
|
+
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
534
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
535
|
+
else {
|
|
536
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
|
|
537
|
+
else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
|
|
538
|
+
else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
|
|
539
|
+
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
540
|
+
else await this.scrollDown(param?.distance || void 0, startingPoint);
|
|
541
|
+
await sleep(500);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
590
544
|
describe() {
|
|
591
545
|
return this.description || `Device ID: ${this.deviceId}`;
|
|
592
546
|
}
|
|
@@ -698,35 +652,31 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
698
652
|
}
|
|
699
653
|
}
|
|
700
654
|
async clearInput(element) {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
debugDevice('Attempting to clear input with WebDriver Clear API');
|
|
706
|
-
const cleared = await this.wdaBackend.clearActiveElement();
|
|
707
|
-
cleared ? debugDevice('Successfully cleared input with WebDriver Clear API') : debugDevice('WebDriver Clear API returned false (no active element or clear failed)');
|
|
655
|
+
await this.clearInputAt(element ? {
|
|
656
|
+
x: element.center[0],
|
|
657
|
+
y: element.center[1]
|
|
658
|
+
} : void 0);
|
|
708
659
|
}
|
|
709
660
|
async url() {
|
|
710
661
|
return '';
|
|
711
662
|
}
|
|
712
663
|
async tap(x, y) {
|
|
713
|
-
await this.
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
await this.tap(x, y);
|
|
718
|
-
}
|
|
719
|
-
async doubleTap(x, y) {
|
|
720
|
-
await this.wdaBackend.doubleTap(Math.round(x), Math.round(y));
|
|
721
|
-
}
|
|
722
|
-
async tripleTap(x, y) {
|
|
723
|
-
await this.wdaBackend.tripleTap(Math.round(x), Math.round(y));
|
|
724
|
-
}
|
|
725
|
-
async longPress(x, y, duration = 1000) {
|
|
726
|
-
await this.wdaBackend.longPress(Math.round(x), Math.round(y), duration);
|
|
664
|
+
await this.tapPoint({
|
|
665
|
+
x,
|
|
666
|
+
y
|
|
667
|
+
});
|
|
727
668
|
}
|
|
728
669
|
async swipe(fromX, fromY, toX, toY, duration = 500) {
|
|
729
|
-
await this.
|
|
670
|
+
await this.swipeCoordinates(fromX, fromY, toX, toY, duration);
|
|
671
|
+
}
|
|
672
|
+
async swipeCoordinates(fromX, fromY, toX, toY, duration = 500) {
|
|
673
|
+
await this.swipePoint({
|
|
674
|
+
x: fromX,
|
|
675
|
+
y: fromY
|
|
676
|
+
}, {
|
|
677
|
+
x: toX,
|
|
678
|
+
y: toY
|
|
679
|
+
}, duration);
|
|
730
680
|
}
|
|
731
681
|
async typeText(text, options) {
|
|
732
682
|
if (!text) return;
|
|
@@ -755,7 +705,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
755
705
|
y: Math.round(height / 2)
|
|
756
706
|
};
|
|
757
707
|
const scrollDistance = Math.round(distance || height / 3);
|
|
758
|
-
await this.
|
|
708
|
+
await this.swipeCoordinates(start.x, start.y, start.x, start.y + scrollDistance);
|
|
759
709
|
}
|
|
760
710
|
async scrollDown(distance, startPoint) {
|
|
761
711
|
const { width, height } = await this.size();
|
|
@@ -767,7 +717,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
767
717
|
y: Math.round(height / 2)
|
|
768
718
|
};
|
|
769
719
|
const scrollDistance = Math.round(distance || height / 3);
|
|
770
|
-
await this.
|
|
720
|
+
await this.swipeCoordinates(start.x, start.y, start.x, start.y - scrollDistance);
|
|
771
721
|
}
|
|
772
722
|
async scrollLeft(distance, startPoint) {
|
|
773
723
|
const { width, height } = await this.size();
|
|
@@ -779,7 +729,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
779
729
|
y: Math.round(height / 2)
|
|
780
730
|
};
|
|
781
731
|
const scrollDistance = Math.round(distance || 0.7 * width);
|
|
782
|
-
await this.
|
|
732
|
+
await this.swipeCoordinates(start.x, start.y, start.x + scrollDistance, start.y);
|
|
783
733
|
}
|
|
784
734
|
async scrollRight(distance, startPoint) {
|
|
785
735
|
const { width, height } = await this.size();
|
|
@@ -791,7 +741,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
791
741
|
y: Math.round(height / 2)
|
|
792
742
|
};
|
|
793
743
|
const scrollDistance = Math.round(distance || 0.7 * width);
|
|
794
|
-
await this.
|
|
744
|
+
await this.swipeCoordinates(start.x, start.y, start.x - scrollDistance, start.y);
|
|
795
745
|
}
|
|
796
746
|
async scrollUntilTop(startPoint) {
|
|
797
747
|
debugDevice('Using screenshot-based scroll detection for better reliability');
|
|
@@ -887,16 +837,16 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
887
837
|
debugDevice(`Performing scroll: ${direction}, distance: ${scrollDistance}`);
|
|
888
838
|
switch(direction){
|
|
889
839
|
case 'up':
|
|
890
|
-
await this.
|
|
840
|
+
await this.swipeCoordinates(start.x, start.y, start.x, start.y + scrollDistance, 300);
|
|
891
841
|
break;
|
|
892
842
|
case 'down':
|
|
893
|
-
await this.
|
|
843
|
+
await this.swipeCoordinates(start.x, start.y, start.x, start.y - scrollDistance, 300);
|
|
894
844
|
break;
|
|
895
845
|
case 'left':
|
|
896
|
-
await this.
|
|
846
|
+
await this.swipeCoordinates(start.x, start.y, start.x + scrollDistance, start.y, 300);
|
|
897
847
|
break;
|
|
898
848
|
case 'right':
|
|
899
|
-
await this.
|
|
849
|
+
await this.swipeCoordinates(start.x, start.y, start.x - scrollDistance, start.y, 300);
|
|
900
850
|
break;
|
|
901
851
|
}
|
|
902
852
|
debugDevice('Waiting for scroll and inertia to complete...');
|
|
@@ -953,7 +903,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
953
903
|
const centerX = Math.round(windowSize.width / 2);
|
|
954
904
|
const startY = Math.round(0.9 * windowSize.height);
|
|
955
905
|
const endY = Math.round(0.5 * windowSize.height);
|
|
956
|
-
await this.
|
|
906
|
+
await this.swipeCoordinates(centerX, startY, centerX, endY, 300);
|
|
957
907
|
debugDevice('Dismissed keyboard with swipe up gesture from bottom of screen');
|
|
958
908
|
await sleep(500);
|
|
959
909
|
return true;
|
|
@@ -1030,6 +980,45 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1030
980
|
_define_property(this, "interfaceType", 'ios');
|
|
1031
981
|
_define_property(this, "uri", void 0);
|
|
1032
982
|
_define_property(this, "options", void 0);
|
|
983
|
+
_define_property(this, "inputPrimitives", {
|
|
984
|
+
pointer: {
|
|
985
|
+
tap: (point)=>this.tapPoint(point),
|
|
986
|
+
doubleClick: (point)=>this.doubleTapPoint(point),
|
|
987
|
+
longPress: (point, opts)=>this.longPressPoint(point, opts?.duration),
|
|
988
|
+
dragAndDrop: (from, to)=>this.swipePoint(from, to, 1000)
|
|
989
|
+
},
|
|
990
|
+
keyboard: {
|
|
991
|
+
keyboardPress: (keyName)=>this.pressKey(keyName),
|
|
992
|
+
typeText: async (value, opts)=>{
|
|
993
|
+
const target = opts?.target;
|
|
994
|
+
if (target && opts?.replace !== false) await this.clearInput(target);
|
|
995
|
+
else if (target) await this.tapPoint({
|
|
996
|
+
x: target.center[0],
|
|
997
|
+
y: target.center[1]
|
|
998
|
+
});
|
|
999
|
+
if (opts?.focusOnly) return;
|
|
1000
|
+
await this.typeText(value, opts);
|
|
1001
|
+
},
|
|
1002
|
+
clearInput: (target)=>this.clearInput(target),
|
|
1003
|
+
cursorMove: async (direction, times = 1)=>{
|
|
1004
|
+
const arrowKey = 'left' === direction ? 'ArrowLeft' : 'ArrowRight';
|
|
1005
|
+
for(let i = 0; i < times; i++)await this.pressKey(arrowKey);
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
touch: {
|
|
1009
|
+
swipe: async (start, end, opts)=>{
|
|
1010
|
+
const duration = opts?.duration ?? 300;
|
|
1011
|
+
const repeat = opts?.repeat ?? 1;
|
|
1012
|
+
for(let i = 0; i < repeat; i++)await this.swipePoint(start, end, duration);
|
|
1013
|
+
},
|
|
1014
|
+
pinch: async (center, opts)=>{
|
|
1015
|
+
await this.wdaBackend.pinch(Math.round(center.x), Math.round(center.y), opts.startDistance, opts.endDistance, opts.duration);
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
scroll: {
|
|
1019
|
+
scroll: (param)=>this.performActionScroll(param)
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1033
1022
|
this.deviceId = 'pending-connection';
|
|
1034
1023
|
this.options = options;
|
|
1035
1024
|
this.customActions = options?.customActions;
|