@midscene/ios 0.30.10 → 1.0.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/README.md +11 -11
- package/dist/es/bin.mjs +423 -327
- package/dist/es/index.mjs +150 -64
- package/dist/lib/bin.js +393 -283
- package/dist/lib/index.js +155 -69
- package/dist/types/index.d.ts +88 -14
- package/package.json +8 -7
- package/static/index.html +1 -1
- package/static/static/css/index.d32b7df9.css +2 -0
- package/static/static/css/index.d32b7df9.css.map +1 -0
- package/static/static/js/79.25af61dc.js +611 -0
- package/static/static/js/{931.dc961e99.js.LICENSE.txt → 79.25af61dc.js.LICENSE.txt} +0 -4
- package/static/static/js/79.25af61dc.js.map +1 -0
- package/static/static/js/async/195.0366f6e8.js +3 -0
- package/static/static/js/async/195.0366f6e8.js.map +1 -0
- package/static/static/js/async/{702.60261735.js → 199.f31e52e7.js} +20 -20
- package/static/static/js/async/199.f31e52e7.js.map +1 -0
- package/static/static/js/async/221.591b048e.js +21 -0
- package/static/static/js/async/221.591b048e.js.map +1 -0
- package/static/static/js/async/271.15d46ff8.js +30 -0
- package/static/static/js/async/271.15d46ff8.js.map +1 -0
- package/static/static/js/async/35.2b64fb0f.js +1 -0
- package/static/static/js/async/{644.6bdc4065.js → 467.710fa05a.js} +1 -1
- package/static/static/js/async/652.b5a7c7b4.js +3 -0
- package/static/static/js/async/652.b5a7c7b4.js.map +1 -0
- package/static/static/js/async/856.be9fd814.js +158 -0
- package/static/static/js/async/{212.e243c338.js.map → 856.be9fd814.js.map} +1 -1
- package/static/static/js/async/860.b56301d9.js +2 -0
- package/static/static/js/async/860.b56301d9.js.map +1 -0
- package/static/static/js/async/990.82a78a53.js +26 -0
- package/static/static/js/async/990.82a78a53.js.map +1 -0
- package/static/static/js/index.0930f837.js +10 -0
- package/static/static/js/index.0930f837.js.map +1 -0
- package/static/static/js/lib-react.7b1abe58.js +3 -0
- package/static/static/js/lib-react.7b1abe58.js.map +1 -0
- package/static/static/css/index.44466eb4.css +0 -2
- package/static/static/css/index.44466eb4.css.map +0 -1
- package/static/static/js/931.dc961e99.js +0 -620
- package/static/static/js/931.dc961e99.js.map +0 -1
- package/static/static/js/async/173.9cf6b074.js +0 -3
- package/static/static/js/async/173.9cf6b074.js.map +0 -1
- package/static/static/js/async/212.e243c338.js +0 -158
- package/static/static/js/async/329.f888b505.js +0 -26
- package/static/static/js/async/329.f888b505.js.map +0 -1
- package/static/static/js/async/364.1821e74b.js +0 -30
- package/static/static/js/async/364.1821e74b.js.map +0 -1
- package/static/static/js/async/544.b73fa603.js +0 -2
- package/static/static/js/async/544.b73fa603.js.map +0 -1
- package/static/static/js/async/582.5dccae2d.js +0 -21
- package/static/static/js/async/582.5dccae2d.js.map +0 -1
- package/static/static/js/async/624.45ee2b2c.js +0 -3
- package/static/static/js/async/624.45ee2b2c.js.map +0 -1
- package/static/static/js/async/659.9afd03db.js +0 -21
- package/static/static/js/async/659.9afd03db.js.map +0 -1
- package/static/static/js/async/702.60261735.js.map +0 -1
- package/static/static/js/async/920.7d9a9aa8.js +0 -2
- package/static/static/js/async/920.7d9a9aa8.js.map +0 -1
- package/static/static/js/async/983.8b91303f.js +0 -1
- package/static/static/js/index.2caaacaf.js +0 -10
- package/static/static/js/index.2caaacaf.js.map +0 -1
- package/static/static/js/lib-react.f566a9ed.js +0 -3
- package/static/static/js/lib-react.f566a9ed.js.map +0 -1
- /package/static/static/js/{index.2caaacaf.js.LICENSE.txt → index.0930f837.js.LICENSE.txt} +0 -0
- /package/static/static/js/{lib-react.f566a9ed.js.LICENSE.txt → lib-react.7b1abe58.js.LICENSE.txt} +0 -0
- /package/static/static/wasm/{9e906fbf55e08f98.module.wasm → 9e906fbf.module.wasm} +0 -0
package/dist/lib/index.js
CHANGED
|
@@ -34,11 +34,11 @@ var __webpack_exports__ = {};
|
|
|
34
34
|
__webpack_require__.r(__webpack_exports__);
|
|
35
35
|
__webpack_require__.d(__webpack_exports__, {
|
|
36
36
|
overrideAIConfig: ()=>env_namespaceObject.overrideAIConfig,
|
|
37
|
+
IOSAgent: ()=>IOSAgent,
|
|
38
|
+
checkIOSEnvironment: ()=>checkIOSEnvironment,
|
|
37
39
|
IOSDevice: ()=>IOSDevice,
|
|
38
40
|
IOSWebDriverClient: ()=>IOSWebDriverClient,
|
|
39
|
-
agentFromWebDriverAgent: ()=>agentFromWebDriverAgent
|
|
40
|
-
IOSAgent: ()=>IOSAgent,
|
|
41
|
-
checkIOSEnvironment: ()=>checkIOSEnvironment
|
|
41
|
+
agentFromWebDriverAgent: ()=>agentFromWebDriverAgent
|
|
42
42
|
});
|
|
43
43
|
const external_node_assert_namespaceObject = require("node:assert");
|
|
44
44
|
var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
|
|
@@ -192,9 +192,8 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
192
192
|
this.ensureSession();
|
|
193
193
|
debugIOS('Getting active element');
|
|
194
194
|
try {
|
|
195
|
-
var _response_value, _response_value1;
|
|
196
195
|
const response = await this.makeRequest('GET', `/session/${this.sessionId}/element/active`);
|
|
197
|
-
const elementId =
|
|
196
|
+
const elementId = response.value?.ELEMENT || response.value?.['element-6066-11e4-a52e-4f735466cecf'] || response.ELEMENT || response['element-6066-11e4-a52e-4f735466cecf'];
|
|
198
197
|
if (elementId) {
|
|
199
198
|
debugIOS(`Got active element ID: ${elementId}`);
|
|
200
199
|
return elementId;
|
|
@@ -271,8 +270,17 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
271
270
|
});
|
|
272
271
|
debugIOS(`Tapped at coordinates (${x}, ${y})`);
|
|
273
272
|
} catch (error) {
|
|
274
|
-
debugIOS(`
|
|
275
|
-
|
|
273
|
+
debugIOS(`New tap endpoint failed, trying legacy endpoint: ${error}`);
|
|
274
|
+
try {
|
|
275
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap/0`, {
|
|
276
|
+
x,
|
|
277
|
+
y
|
|
278
|
+
});
|
|
279
|
+
debugIOS(`Tapped at coordinates (${x}, ${y}) using legacy endpoint`);
|
|
280
|
+
} catch (fallbackError) {
|
|
281
|
+
debugIOS(`Failed to tap at (${x}, ${y}): ${fallbackError}`);
|
|
282
|
+
throw new Error(`Failed to tap at coordinates: ${fallbackError}`);
|
|
283
|
+
}
|
|
276
284
|
}
|
|
277
285
|
}
|
|
278
286
|
async swipe(fromX, fromY, toX, toY, duration = 500) {
|
|
@@ -345,13 +353,34 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
345
353
|
debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
|
|
346
354
|
}
|
|
347
355
|
async getScreenScale() {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
356
|
+
this.ensureSession();
|
|
357
|
+
try {
|
|
358
|
+
const screenResponse = await this.makeRequest('GET', `/session/${this.sessionId}/wda/screen`);
|
|
359
|
+
if (screenResponse?.value?.scale) {
|
|
360
|
+
debugIOS(`Got screen scale from WDA screen endpoint: ${screenResponse.value.scale}`);
|
|
361
|
+
return screenResponse.value.scale;
|
|
362
|
+
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
debugIOS(`Failed to get screen scale from /wda/screen: ${error}`);
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
debugIOS('Calculating screen scale from screenshot and window size');
|
|
368
|
+
const [screenshotBase64, windowSize] = await Promise.all([
|
|
369
|
+
this.takeScreenshot(),
|
|
370
|
+
this.getWindowSize()
|
|
371
|
+
]);
|
|
372
|
+
const { jimpFromBase64 } = await import("@midscene/shared/img");
|
|
373
|
+
const screenshotImg = await jimpFromBase64(screenshotBase64);
|
|
374
|
+
const screenshotWidth = screenshotImg.bitmap.width;
|
|
375
|
+
const screenshotHeight = screenshotImg.bitmap.height;
|
|
376
|
+
const scale = Math.max(screenshotWidth, screenshotHeight) / Math.max(windowSize.width, windowSize.height);
|
|
377
|
+
const roundedScale = Math.round(scale);
|
|
378
|
+
debugIOS(`Calculated screen scale: ${roundedScale} (screenshot: ${screenshotWidth}x${screenshotHeight}, window: ${windowSize.width}x${windowSize.height})`);
|
|
379
|
+
return roundedScale;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
debugIOS(`Failed to calculate screen scale: ${error}`);
|
|
353
382
|
}
|
|
354
|
-
debugIOS('No screen scale found
|
|
383
|
+
debugIOS('No screen scale found');
|
|
355
384
|
return null;
|
|
356
385
|
}
|
|
357
386
|
async createSession(capabilities) {
|
|
@@ -378,6 +407,10 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
378
407
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
379
408
|
}
|
|
380
409
|
}
|
|
410
|
+
async executeRequest(method, endpoint, data) {
|
|
411
|
+
this.ensureSession();
|
|
412
|
+
return this.makeRequest(method, endpoint, data);
|
|
413
|
+
}
|
|
381
414
|
}
|
|
382
415
|
function _define_property(obj, key, value) {
|
|
383
416
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -390,6 +423,12 @@ function _define_property(obj, key, value) {
|
|
|
390
423
|
return obj;
|
|
391
424
|
}
|
|
392
425
|
const debugDevice = (0, logger_namespaceObject.getDebug)('ios:device');
|
|
426
|
+
const WDA_HTTP_METHODS = [
|
|
427
|
+
'GET',
|
|
428
|
+
'POST',
|
|
429
|
+
'DELETE',
|
|
430
|
+
'PUT'
|
|
431
|
+
];
|
|
393
432
|
class IOSDevice {
|
|
394
433
|
actionSpace() {
|
|
395
434
|
const defaultActions = [
|
|
@@ -409,7 +448,7 @@ class IOSDevice {
|
|
|
409
448
|
interfaceAlias: 'aiInput',
|
|
410
449
|
paramSchema: core_namespaceObject.z.object({
|
|
411
450
|
value: core_namespaceObject.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.'),
|
|
412
|
-
autoDismissKeyboard: core_namespaceObject.z.boolean().optional().describe('
|
|
451
|
+
autoDismissKeyboard: core_namespaceObject.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.'),
|
|
413
452
|
mode: core_namespaceObject.z["enum"]([
|
|
414
453
|
'replace',
|
|
415
454
|
'clear',
|
|
@@ -418,14 +457,13 @@ class IOSDevice {
|
|
|
418
457
|
locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
|
|
419
458
|
}),
|
|
420
459
|
call: async (param)=>{
|
|
421
|
-
var _this_options;
|
|
422
460
|
const element = param.locate;
|
|
423
461
|
if (element) {
|
|
424
462
|
if ('append' !== param.mode) await this.clearInput(element);
|
|
425
463
|
}
|
|
426
464
|
if ('clear' === param.mode) return;
|
|
427
465
|
if (!param || !param.value) return;
|
|
428
|
-
const autoDismissKeyboard = param.autoDismissKeyboard ??
|
|
466
|
+
const autoDismissKeyboard = param.autoDismissKeyboard ?? this.options?.autoDismissKeyboard;
|
|
429
467
|
await this.typeText(param.value, {
|
|
430
468
|
autoDismissKeyboard
|
|
431
469
|
});
|
|
@@ -437,18 +475,18 @@ class IOSDevice {
|
|
|
437
475
|
left: element.center[0],
|
|
438
476
|
top: element.center[1]
|
|
439
477
|
} : void 0;
|
|
440
|
-
const scrollToEventName =
|
|
441
|
-
if ('
|
|
442
|
-
else if ('
|
|
443
|
-
else if ('
|
|
444
|
-
else if ('
|
|
445
|
-
else if ('
|
|
478
|
+
const scrollToEventName = param?.scrollType;
|
|
479
|
+
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
480
|
+
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
481
|
+
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
482
|
+
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
483
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
446
484
|
else {
|
|
447
|
-
if (
|
|
485
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
|
|
448
486
|
else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
|
|
449
487
|
else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
|
|
450
488
|
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
451
|
-
else await this.scrollDown(
|
|
489
|
+
else await this.scrollDown(param?.distance || void 0, startingPoint);
|
|
452
490
|
await (0, utils_namespaceObject.sleep)(500);
|
|
453
491
|
}
|
|
454
492
|
}),
|
|
@@ -462,22 +500,6 @@ class IOSDevice {
|
|
|
462
500
|
(0, device_namespaceObject.defineActionKeyboardPress)(async (param)=>{
|
|
463
501
|
await this.pressKey(param.keyName);
|
|
464
502
|
}),
|
|
465
|
-
(0, device_namespaceObject.defineAction)({
|
|
466
|
-
name: 'IOSHomeButton',
|
|
467
|
-
description: 'Trigger the system "home" operation on iOS devices',
|
|
468
|
-
paramSchema: core_namespaceObject.z.object({}),
|
|
469
|
-
call: async ()=>{
|
|
470
|
-
await this.home();
|
|
471
|
-
}
|
|
472
|
-
}),
|
|
473
|
-
(0, device_namespaceObject.defineAction)({
|
|
474
|
-
name: 'IOSAppSwitcher',
|
|
475
|
-
description: 'Trigger the system "app switcher" operation on iOS devices',
|
|
476
|
-
paramSchema: core_namespaceObject.z.object({}),
|
|
477
|
-
call: async ()=>{
|
|
478
|
-
await this.appSwitcher();
|
|
479
|
-
}
|
|
480
|
-
}),
|
|
481
503
|
(0, device_namespaceObject.defineAction)({
|
|
482
504
|
name: 'IOSLongPress',
|
|
483
505
|
description: 'Trigger a long press on the screen at specified coordinates on iOS devices',
|
|
@@ -489,7 +511,7 @@ class IOSDevice {
|
|
|
489
511
|
const element = param.locate;
|
|
490
512
|
external_node_assert_default()(element, 'IOSLongPress requires an element to be located');
|
|
491
513
|
const [x, y] = element.center;
|
|
492
|
-
await this.longPress(x, y,
|
|
514
|
+
await this.longPress(x, y, param?.duration);
|
|
493
515
|
}
|
|
494
516
|
}),
|
|
495
517
|
(0, device_namespaceObject.defineActionClearInput)(async (param)=>{
|
|
@@ -498,9 +520,11 @@ class IOSDevice {
|
|
|
498
520
|
await this.clearInput(element);
|
|
499
521
|
})
|
|
500
522
|
];
|
|
523
|
+
const platformSpecificActions = Object.values(createPlatformActions(this));
|
|
501
524
|
const customActions = this.customActions || [];
|
|
502
525
|
return [
|
|
503
526
|
...defaultActions,
|
|
527
|
+
...platformSpecificActions,
|
|
504
528
|
...customActions
|
|
505
529
|
];
|
|
506
530
|
}
|
|
@@ -517,7 +541,7 @@ class IOSDevice {
|
|
|
517
541
|
await this.wdaManager.start();
|
|
518
542
|
await this.wdaBackend.createSession();
|
|
519
543
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
520
|
-
if (
|
|
544
|
+
if (deviceInfo?.udid) {
|
|
521
545
|
this.deviceId = deviceInfo.udid;
|
|
522
546
|
debugDevice(`Updated device ID to real UDID: ${this.deviceId}`);
|
|
523
547
|
}
|
|
@@ -625,9 +649,8 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
625
649
|
await this.wdaBackend.swipe(Math.round(fromX), Math.round(fromY), Math.round(toX), Math.round(toY), duration);
|
|
626
650
|
}
|
|
627
651
|
async typeText(text, options) {
|
|
628
|
-
var _this_options;
|
|
629
652
|
if (!text) return;
|
|
630
|
-
const shouldAutoDismissKeyboard =
|
|
653
|
+
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
631
654
|
debugDevice(`Typing text: "${text}"`);
|
|
632
655
|
try {
|
|
633
656
|
await (0, utils_namespaceObject.sleep)(200);
|
|
@@ -829,20 +852,30 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
829
852
|
}
|
|
830
853
|
async hideKeyboard(keyNames) {
|
|
831
854
|
try {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
855
|
+
const dismissKeys = keyNames && keyNames.length > 0 ? keyNames : [
|
|
856
|
+
'return',
|
|
857
|
+
'done',
|
|
858
|
+
'go',
|
|
859
|
+
'search',
|
|
860
|
+
'next',
|
|
861
|
+
'send'
|
|
862
|
+
];
|
|
863
|
+
debugDevice(`Attempting to dismiss keyboard using WDA API with keys: ${dismissKeys.join(', ')}`);
|
|
864
|
+
try {
|
|
865
|
+
await this.wdaBackend.dismissKeyboard(dismissKeys);
|
|
866
|
+
debugDevice('Successfully dismissed keyboard using WDA API');
|
|
867
|
+
await (0, utils_namespaceObject.sleep)(500);
|
|
837
868
|
return true;
|
|
869
|
+
} catch (wdaError) {
|
|
870
|
+
debugDevice(`WDA dismissKeyboard failed, falling back to swipe gesture: ${wdaError}`);
|
|
838
871
|
}
|
|
839
872
|
const windowSize = await this.wdaBackend.getWindowSize();
|
|
840
873
|
const centerX = Math.round(windowSize.width / 2);
|
|
841
|
-
const startY = Math.round(0.
|
|
842
|
-
const endY = Math.round(0.
|
|
843
|
-
await this.swipe(centerX, startY, centerX, endY,
|
|
844
|
-
debugDevice('Dismissed keyboard with swipe
|
|
845
|
-
await (0, utils_namespaceObject.sleep)(
|
|
874
|
+
const startY = Math.round(0.9 * windowSize.height);
|
|
875
|
+
const endY = Math.round(0.5 * windowSize.height);
|
|
876
|
+
await this.swipe(centerX, startY, centerX, endY, 300);
|
|
877
|
+
debugDevice('Dismissed keyboard with swipe up gesture from bottom of screen');
|
|
878
|
+
await (0, utils_namespaceObject.sleep)(500);
|
|
846
879
|
return true;
|
|
847
880
|
} catch (error) {
|
|
848
881
|
debugDevice(`Failed to hide keyboard: ${error}`);
|
|
@@ -871,6 +904,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
871
904
|
async openUrlViaSafari(url) {
|
|
872
905
|
try {
|
|
873
906
|
debugDevice(`Opening URL via Safari: ${url}`);
|
|
907
|
+
await this.wdaBackend.terminateApp('com.apple.mobilesafari');
|
|
874
908
|
await this.wdaBackend.launchApp('com.apple.mobilesafari');
|
|
875
909
|
await (0, utils_namespaceObject.sleep)(2000);
|
|
876
910
|
await this.typeText(url);
|
|
@@ -888,6 +922,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
888
922
|
throw new Error(`Failed to open URL via Safari: ${error}`);
|
|
889
923
|
}
|
|
890
924
|
}
|
|
925
|
+
async runWdaRequest(method, endpoint, data) {
|
|
926
|
+
return await this.wdaBackend.executeRequest(method, endpoint, data);
|
|
927
|
+
}
|
|
891
928
|
async destroy() {
|
|
892
929
|
if (this.destroyed) return;
|
|
893
930
|
try {
|
|
@@ -913,9 +950,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
913
950
|
_define_property(this, "options", void 0);
|
|
914
951
|
this.deviceId = 'pending-connection';
|
|
915
952
|
this.options = options;
|
|
916
|
-
this.customActions =
|
|
917
|
-
const wdaPort =
|
|
918
|
-
const wdaHost =
|
|
953
|
+
this.customActions = options?.customActions;
|
|
954
|
+
const wdaPort = options?.wdaPort || constants_namespaceObject.DEFAULT_WDA_PORT;
|
|
955
|
+
const wdaHost = options?.wdaHost || 'localhost';
|
|
919
956
|
this.wdaBackend = new IOSWebDriverClient({
|
|
920
957
|
port: wdaPort,
|
|
921
958
|
host: wdaHost
|
|
@@ -923,6 +960,44 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
923
960
|
this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
|
|
924
961
|
}
|
|
925
962
|
}
|
|
963
|
+
const runWdaRequestParamSchema = core_namespaceObject.z.object({
|
|
964
|
+
method: core_namespaceObject.z["enum"](WDA_HTTP_METHODS).describe('HTTP method (GET, POST, DELETE, PUT)'),
|
|
965
|
+
endpoint: core_namespaceObject.z.string().describe('WebDriver API endpoint'),
|
|
966
|
+
data: core_namespaceObject.z.object({}).passthrough().optional().describe('Optional request body data as JSON object')
|
|
967
|
+
});
|
|
968
|
+
const launchParamSchema = core_namespaceObject.z.string().describe('App bundle ID or URL to launch');
|
|
969
|
+
const createPlatformActions = (device)=>({
|
|
970
|
+
RunWdaRequest: (0, device_namespaceObject.defineAction)({
|
|
971
|
+
name: 'RunWdaRequest',
|
|
972
|
+
description: 'Execute WebDriverAgent API request directly on iOS device',
|
|
973
|
+
interfaceAlias: 'runWdaRequest',
|
|
974
|
+
paramSchema: runWdaRequestParamSchema,
|
|
975
|
+
call: async (param)=>await device.runWdaRequest(param.method, param.endpoint, param.data)
|
|
976
|
+
}),
|
|
977
|
+
Launch: (0, device_namespaceObject.defineAction)({
|
|
978
|
+
name: 'Launch',
|
|
979
|
+
description: 'Launch an iOS app or URL',
|
|
980
|
+
interfaceAlias: 'launch',
|
|
981
|
+
paramSchema: launchParamSchema,
|
|
982
|
+
call: async (param)=>{
|
|
983
|
+
await device.launch(param);
|
|
984
|
+
}
|
|
985
|
+
}),
|
|
986
|
+
IOSHomeButton: (0, device_namespaceObject.defineAction)({
|
|
987
|
+
name: 'IOSHomeButton',
|
|
988
|
+
description: 'Trigger the system "home" operation on iOS devices',
|
|
989
|
+
call: async ()=>{
|
|
990
|
+
await device.home();
|
|
991
|
+
}
|
|
992
|
+
}),
|
|
993
|
+
IOSAppSwitcher: (0, device_namespaceObject.defineAction)({
|
|
994
|
+
name: 'IOSAppSwitcher',
|
|
995
|
+
description: 'Trigger the system "app switcher" operation on iOS devices',
|
|
996
|
+
call: async ()=>{
|
|
997
|
+
await device.appSwitcher();
|
|
998
|
+
}
|
|
999
|
+
})
|
|
1000
|
+
});
|
|
926
1001
|
const agent_namespaceObject = require("@midscene/core/agent");
|
|
927
1002
|
const external_node_child_process_namespaceObject = require("node:child_process");
|
|
928
1003
|
const external_node_os_namespaceObject = require("node:os");
|
|
@@ -973,24 +1048,35 @@ async function checkIOSEnvironment() {
|
|
|
973
1048
|
};
|
|
974
1049
|
}
|
|
975
1050
|
}
|
|
1051
|
+
function agent_define_property(obj, key, value) {
|
|
1052
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
1053
|
+
value: value,
|
|
1054
|
+
enumerable: true,
|
|
1055
|
+
configurable: true,
|
|
1056
|
+
writable: true
|
|
1057
|
+
});
|
|
1058
|
+
else obj[key] = value;
|
|
1059
|
+
return obj;
|
|
1060
|
+
}
|
|
976
1061
|
const debugAgent = (0, logger_namespaceObject.getDebug)('ios:agent');
|
|
977
1062
|
class IOSAgent extends agent_namespaceObject.Agent {
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
|
|
1063
|
+
createActionWrapper(name) {
|
|
1064
|
+
const action = this.wrapActionInActionSpace(name);
|
|
1065
|
+
return (...args)=>action(args[0]);
|
|
1066
|
+
}
|
|
1067
|
+
constructor(device, opts){
|
|
1068
|
+
super(device, opts), agent_define_property(this, "launch", void 0), agent_define_property(this, "runWdaRequest", void 0), agent_define_property(this, "home", void 0), agent_define_property(this, "appSwitcher", void 0);
|
|
1069
|
+
this.launch = this.createActionWrapper('Launch');
|
|
1070
|
+
this.runWdaRequest = this.createActionWrapper('RunWdaRequest');
|
|
1071
|
+
this.home = this.createActionWrapper('IOSHomeButton');
|
|
1072
|
+
this.appSwitcher = this.createActionWrapper('IOSAppSwitcher');
|
|
981
1073
|
}
|
|
982
1074
|
}
|
|
983
1075
|
async function agentFromWebDriverAgent(opts) {
|
|
984
1076
|
debugAgent('Creating iOS agent with WebDriverAgent auto-detection');
|
|
985
1077
|
const envCheck = await checkIOSEnvironment();
|
|
986
1078
|
if (!envCheck.available) throw new Error(`iOS environment not available: ${envCheck.error}`);
|
|
987
|
-
const device = new IOSDevice({
|
|
988
|
-
autoDismissKeyboard: null == opts ? void 0 : opts.autoDismissKeyboard,
|
|
989
|
-
customActions: null == opts ? void 0 : opts.customActions,
|
|
990
|
-
wdaPort: null == opts ? void 0 : opts.wdaPort,
|
|
991
|
-
wdaHost: null == opts ? void 0 : opts.wdaHost,
|
|
992
|
-
useWDA: null == opts ? void 0 : opts.useWDA
|
|
993
|
-
});
|
|
1079
|
+
const device = new IOSDevice(opts || {});
|
|
994
1080
|
await device.connect();
|
|
995
1081
|
return new IOSAgent(device, opts);
|
|
996
1082
|
}
|
|
@@ -1001,14 +1087,14 @@ exports.IOSWebDriverClient = __webpack_exports__.IOSWebDriverClient;
|
|
|
1001
1087
|
exports.agentFromWebDriverAgent = __webpack_exports__.agentFromWebDriverAgent;
|
|
1002
1088
|
exports.checkIOSEnvironment = __webpack_exports__.checkIOSEnvironment;
|
|
1003
1089
|
exports.overrideAIConfig = __webpack_exports__.overrideAIConfig;
|
|
1004
|
-
for(var
|
|
1090
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
1005
1091
|
"IOSAgent",
|
|
1006
1092
|
"IOSDevice",
|
|
1007
1093
|
"IOSWebDriverClient",
|
|
1008
1094
|
"agentFromWebDriverAgent",
|
|
1009
1095
|
"checkIOSEnvironment",
|
|
1010
1096
|
"overrideAIConfig"
|
|
1011
|
-
].indexOf(
|
|
1097
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
1012
1098
|
Object.defineProperty(exports, '__esModule', {
|
|
1013
1099
|
value: true
|
|
1014
1100
|
});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { AbstractInterface } from '@midscene/core/device';
|
|
2
|
+
import type { ActionParam } from '@midscene/core';
|
|
3
|
+
import type { ActionReturn } from '@midscene/core';
|
|
2
4
|
import { Agent } from '@midscene/core/agent';
|
|
3
5
|
import { AgentOpt } from '@midscene/core/agent';
|
|
4
6
|
import { DeviceAction } from '@midscene/core';
|
|
5
7
|
import type { ElementInfo } from '@midscene/shared/extractor';
|
|
6
8
|
import { InterfaceType } from '@midscene/core';
|
|
9
|
+
import { IOSDeviceInputOpt } from '@midscene/core/device';
|
|
10
|
+
import { IOSDeviceOpt } from '@midscene/core/device';
|
|
7
11
|
import { overrideAIConfig } from '@midscene/shared/env';
|
|
8
12
|
import { Point } from '@midscene/core';
|
|
9
13
|
import { Size } from '@midscene/core';
|
|
10
14
|
import { WebDriverClient } from '@midscene/webdriver';
|
|
15
|
+
import { z } from '@midscene/core';
|
|
16
|
+
|
|
17
|
+
declare type ActionArgs<T extends DeviceAction> = [ActionParam<T>] extends [undefined] ? [] : [ActionParam<T>];
|
|
11
18
|
|
|
12
19
|
export declare function agentFromWebDriverAgent(opts?: IOSAgentOpt & IOSDeviceOpt): Promise<IOSAgent>;
|
|
13
20
|
|
|
@@ -16,11 +23,38 @@ export declare function checkIOSEnvironment(): Promise<{
|
|
|
16
23
|
error?: string;
|
|
17
24
|
}>;
|
|
18
25
|
|
|
26
|
+
declare type DeviceActionIOSAppSwitcher = DeviceAction<undefined, void>;
|
|
27
|
+
|
|
28
|
+
declare type DeviceActionIOSHomeButton = DeviceAction<undefined, void>;
|
|
29
|
+
|
|
30
|
+
declare type DeviceActionLaunch = DeviceAction<LaunchParam, void>;
|
|
31
|
+
|
|
32
|
+
declare type DeviceActionRunWdaRequest = DeviceAction<RunWdaRequestParam, RunWdaRequestReturn>;
|
|
33
|
+
|
|
19
34
|
export declare class IOSAgent extends Agent<IOSDevice> {
|
|
20
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Launch an iOS app or URL
|
|
37
|
+
* Type-safe wrapper around the Launch action from actionSpace
|
|
38
|
+
*/
|
|
39
|
+
launch: WrappedAction<DeviceActionLaunch>;
|
|
40
|
+
/**
|
|
41
|
+
* Execute WebDriverAgent API request directly
|
|
42
|
+
* Type-safe wrapper around the RunWdaRequest action from actionSpace
|
|
43
|
+
*/
|
|
44
|
+
runWdaRequest: WrappedAction<DeviceActionRunWdaRequest>;
|
|
45
|
+
/**
|
|
46
|
+
* Trigger the system home operation on iOS devices
|
|
47
|
+
*/
|
|
48
|
+
home: WrappedAction<DeviceActionIOSHomeButton>;
|
|
49
|
+
/**
|
|
50
|
+
* Trigger the system app switcher operation on iOS devices
|
|
51
|
+
*/
|
|
52
|
+
appSwitcher: WrappedAction<DeviceActionIOSAppSwitcher>;
|
|
53
|
+
constructor(device: IOSDevice, opts?: IOSAgentOpt);
|
|
54
|
+
private createActionWrapper;
|
|
21
55
|
}
|
|
22
56
|
|
|
23
|
-
declare type IOSAgentOpt = AgentOpt;
|
|
57
|
+
export declare type IOSAgentOpt = AgentOpt;
|
|
24
58
|
|
|
25
59
|
export declare class IOSDevice implements AbstractInterface {
|
|
26
60
|
private deviceId;
|
|
@@ -91,21 +125,18 @@ export declare class IOSDevice implements AbstractInterface {
|
|
|
91
125
|
* @param url The URL to open
|
|
92
126
|
*/
|
|
93
127
|
openUrlViaSafari(url: string): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Execute a WebDriverAgent API request directly
|
|
130
|
+
* This is the iOS equivalent of Android's runAdbShell
|
|
131
|
+
* @param method HTTP method (GET, POST, DELETE, PUT)
|
|
132
|
+
* @param endpoint WebDriver API endpoint
|
|
133
|
+
* @param data Optional request body data
|
|
134
|
+
* @returns Response from the WebDriver API
|
|
135
|
+
*/
|
|
136
|
+
runWdaRequest<TResult = any>(method: WDAHttpMethod, endpoint: string, data?: any): Promise<TResult>;
|
|
94
137
|
destroy(): Promise<void>;
|
|
95
138
|
}
|
|
96
139
|
|
|
97
|
-
declare type IOSDeviceInputOpt = {
|
|
98
|
-
autoDismissKeyboard?: boolean;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
declare type IOSDeviceOpt = {
|
|
102
|
-
deviceId?: string;
|
|
103
|
-
customActions?: DeviceAction<any>[];
|
|
104
|
-
wdaPort?: number;
|
|
105
|
-
wdaHost?: string;
|
|
106
|
-
useWDA?: boolean;
|
|
107
|
-
} & IOSDeviceInputOpt;
|
|
108
|
-
|
|
109
140
|
export declare class IOSWebDriverClient extends WebDriverClient {
|
|
110
141
|
launchApp(bundleId: string): Promise<void>;
|
|
111
142
|
activateApp(bundleId: string): Promise<void>;
|
|
@@ -140,8 +171,51 @@ export declare class IOSWebDriverClient extends WebDriverClient {
|
|
|
140
171
|
getScreenScale(): Promise<number | null>;
|
|
141
172
|
createSession(capabilities?: any): Promise<any>;
|
|
142
173
|
private setupIOSSession;
|
|
174
|
+
/**
|
|
175
|
+
* Execute a WebDriverAgent API request directly
|
|
176
|
+
* This is the iOS equivalent of Android's runAdbShell
|
|
177
|
+
* @param method HTTP method (GET, POST, DELETE, etc.)
|
|
178
|
+
* @param endpoint WebDriver API endpoint
|
|
179
|
+
* @param data Optional request body data
|
|
180
|
+
* @returns Response from the WebDriver API
|
|
181
|
+
*/
|
|
182
|
+
executeRequest<TResult = any>(method: string, endpoint: string, data?: any): Promise<TResult>;
|
|
143
183
|
}
|
|
144
184
|
|
|
185
|
+
declare type LaunchParam = z.infer<typeof launchParamSchema>;
|
|
186
|
+
|
|
187
|
+
declare const launchParamSchema: z.ZodString;
|
|
188
|
+
|
|
145
189
|
export { overrideAIConfig }
|
|
146
190
|
|
|
191
|
+
declare type RunWdaRequestParam = z.infer<typeof runWdaRequestParamSchema>;
|
|
192
|
+
|
|
193
|
+
declare const runWdaRequestParamSchema: z.ZodObject<{
|
|
194
|
+
method: z.ZodEnum<["GET", "POST", "DELETE", "PUT"]>;
|
|
195
|
+
endpoint: z.ZodString;
|
|
196
|
+
data: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
|
|
197
|
+
}, "strip", z.ZodTypeAny, {
|
|
198
|
+
method: "POST" | "GET" | "DELETE" | "PUT";
|
|
199
|
+
endpoint: string;
|
|
200
|
+
data?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
201
|
+
}, {
|
|
202
|
+
method: "POST" | "GET" | "DELETE" | "PUT";
|
|
203
|
+
endpoint: string;
|
|
204
|
+
data?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
205
|
+
}>;
|
|
206
|
+
|
|
207
|
+
declare type RunWdaRequestReturn = Awaited<ReturnType<IOSDevice['runWdaRequest']>>;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* HTTP methods supported by WebDriverAgent API
|
|
211
|
+
*/
|
|
212
|
+
declare const WDA_HTTP_METHODS: readonly ["GET", "POST", "DELETE", "PUT"];
|
|
213
|
+
|
|
214
|
+
declare type WDAHttpMethod = (typeof WDA_HTTP_METHODS)[number];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Helper type to convert DeviceAction to wrapped method signature
|
|
218
|
+
*/
|
|
219
|
+
declare type WrappedAction<T extends DeviceAction> = (...args: ActionArgs<T>) => Promise<ActionReturn<T>>;
|
|
220
|
+
|
|
147
221
|
export { }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/ios",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "iOS automation library for Midscene",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iOS UI automation",
|
|
@@ -38,24 +38,25 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@inquirer/prompts": "^7.8.6",
|
|
40
40
|
"open": "10.1.0",
|
|
41
|
-
"@midscene/
|
|
42
|
-
"@midscene/
|
|
43
|
-
"@midscene/webdriver": "0.
|
|
41
|
+
"@midscene/core": "1.0.0",
|
|
42
|
+
"@midscene/shared": "1.0.0",
|
|
43
|
+
"@midscene/webdriver": "1.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@rslib/core": "^0.
|
|
46
|
+
"@rslib/core": "^0.18.3",
|
|
47
47
|
"@types/node": "^18.0.0",
|
|
48
48
|
"dotenv": "^16.4.5",
|
|
49
49
|
"typescript": "^5.8.3",
|
|
50
50
|
"tsx": "^4.19.2",
|
|
51
51
|
"vitest": "3.0.5",
|
|
52
|
-
"
|
|
52
|
+
"zod": "3.24.3",
|
|
53
|
+
"@midscene/playground": "1.0.0"
|
|
53
54
|
},
|
|
54
55
|
"license": "MIT",
|
|
55
56
|
"scripts": {
|
|
56
57
|
"dev": "npm run build:watch",
|
|
57
58
|
"build": "rslib build",
|
|
58
|
-
"build:watch": "rslib build --watch",
|
|
59
|
+
"build:watch": "rslib build --watch --no-clean",
|
|
59
60
|
"playground": "DEBUG=midscene:* tsx demo/playground.ts",
|
|
60
61
|
"test": "vitest --run",
|
|
61
62
|
"test:u": "vitest --run -u",
|
package/static/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html><head><link rel="icon" href="/favicon.ico"><title>Midscene Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.
|
|
1
|
+
<!doctype html><html><head><link rel="icon" href="/favicon.ico"><title>Midscene Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.7b1abe58.js"></script><script defer src="/static/js/79.25af61dc.js"></script><script defer src="/static/js/index.0930f837.js"></script><link href="/static/css/index.d32b7df9.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
|