appium-xcuitest-driver 11.2.4 → 11.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [11.3.1](https://github.com/appium/appium-xcuitest-driver/compare/v11.3.0...v11.3.1) (2026-05-12)
2
+
3
+ ### Miscellaneous Chores
4
+
5
+ * bump remotexpc to 1.1.8 ([#2838](https://github.com/appium/appium-xcuitest-driver/issues/2838)) ([46fc046](https://github.com/appium/appium-xcuitest-driver/commit/46fc046c47202b8c17c8d3cd2f6cee75b350e436))
6
+
7
+ ## [11.3.0](https://github.com/appium/appium-xcuitest-driver/compare/v11.2.4...v11.3.0) (2026-05-09)
8
+
9
+ ### Features
10
+
11
+ * add download-wda command ([#2835](https://github.com/appium/appium-xcuitest-driver/issues/2835)) ([1f568b4](https://github.com/appium/appium-xcuitest-driver/commit/1f568b4823a75ede182afdc7c82b73193f554765))
12
+
1
13
  ## [11.2.4](https://github.com/appium/appium-xcuitest-driver/compare/v11.2.3...v11.2.4) (2026-05-08)
2
14
 
3
15
  ### Bug Fixes
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appium-xcuitest-driver",
3
- "version": "11.2.4",
3
+ "version": "11.3.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appium-xcuitest-driver",
9
- "version": "11.2.4",
9
+ "version": "11.3.1",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@appium/strongbox": "^1.0.0-rc.1",
@@ -65,7 +65,7 @@
65
65
  "npm": ">=10"
66
66
  },
67
67
  "optionalDependencies": {
68
- "appium-ios-remotexpc": "^1.1.6"
68
+ "appium-ios-remotexpc": "^1.1.8"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "appium": "^3.0.0-rc.2"
@@ -104,6 +104,19 @@
104
104
  "spdy": "4.0.2"
105
105
  }
106
106
  },
107
+ "node_modules/@appium/base-driver/node_modules/asyncbox": {
108
+ "version": "6.2.0",
109
+ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.2.0.tgz",
110
+ "integrity": "sha512-z1XpHkoT3y+1aXfazEY5d7HN2eOi50fLq7ZTxG0H4WegLxrtEAI5Vsc6OR9dOwoC3SJQLXyV0ZVnPEh6GIgMKQ==",
111
+ "license": "Apache-2.0",
112
+ "dependencies": {
113
+ "p-limit": "^7.2.0"
114
+ },
115
+ "engines": {
116
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
117
+ "npm": ">=10"
118
+ }
119
+ },
107
120
  "node_modules/@appium/base-driver/node_modules/lru-cache": {
108
121
  "version": "11.3.5",
109
122
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz",
@@ -240,6 +253,31 @@
240
253
  "sharp": "0.34.5"
241
254
  }
242
255
  },
256
+ "node_modules/@appium/support/node_modules/asyncbox": {
257
+ "version": "6.2.0",
258
+ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.2.0.tgz",
259
+ "integrity": "sha512-z1XpHkoT3y+1aXfazEY5d7HN2eOi50fLq7ZTxG0H4WegLxrtEAI5Vsc6OR9dOwoC3SJQLXyV0ZVnPEh6GIgMKQ==",
260
+ "license": "Apache-2.0",
261
+ "dependencies": {
262
+ "p-limit": "^7.2.0"
263
+ },
264
+ "engines": {
265
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
266
+ "npm": ">=10"
267
+ }
268
+ },
269
+ "node_modules/@appium/support/node_modules/semver": {
270
+ "version": "7.7.4",
271
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
272
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
273
+ "license": "ISC",
274
+ "bin": {
275
+ "semver": "bin/semver.js"
276
+ },
277
+ "engines": {
278
+ "node": ">=10"
279
+ }
280
+ },
243
281
  "node_modules/@appium/tsconfig": {
244
282
  "version": "1.1.2",
245
283
  "resolved": "https://registry.npmjs.org/@appium/tsconfig/-/tsconfig-1.1.2.tgz",
@@ -551,15 +589,14 @@
551
589
  }
552
590
  },
553
591
  "node_modules/appium-ios-device": {
554
- "version": "3.1.12",
555
- "resolved": "https://registry.npmjs.org/appium-ios-device/-/appium-ios-device-3.1.12.tgz",
556
- "integrity": "sha512-2EEeGzWz2uDmUJftxuZ4b5DCptGqvQuND+FDMQ6p4qOpxbQX6E5HmAs0LyeepB/xAxkUP9wXZdBRl1OnrLKqvg==",
592
+ "version": "3.1.14",
593
+ "resolved": "https://registry.npmjs.org/appium-ios-device/-/appium-ios-device-3.1.14.tgz",
594
+ "integrity": "sha512-fjqh1oPYQ2C4v96OLhom3HCK6j2NRO0TYOgZXtT9vEEjug/Z25NAjxlLnQLBe7aAHxR2c0V/SyFmLTEBs/Ih5A==",
557
595
  "license": "Apache-2.0",
558
596
  "dependencies": {
559
597
  "@appium/support": "^7.2.2",
560
598
  "asyncbox": "^6.0.1",
561
599
  "axios": "^1.16.0",
562
- "bluebird": "^3.1.1",
563
600
  "bplist-creator": "^0.x",
564
601
  "bplist-parser": "^0.x",
565
602
  "lodash": "^4.17.15",
@@ -571,9 +608,9 @@
571
608
  }
572
609
  },
573
610
  "node_modules/appium-ios-remotexpc": {
574
- "version": "1.1.6",
575
- "resolved": "https://registry.npmjs.org/appium-ios-remotexpc/-/appium-ios-remotexpc-1.1.6.tgz",
576
- "integrity": "sha512-jDkqVoP/7WM1g2z9p6aN+Yveek7yQ7WICdoaTnTfpsr5eF5/Luaxlk7okglp4GfdsOK6G4OUBYtCby99WRTczg==",
611
+ "version": "1.1.8",
612
+ "resolved": "https://registry.npmjs.org/appium-ios-remotexpc/-/appium-ios-remotexpc-1.1.8.tgz",
613
+ "integrity": "sha512-62j5AqfjgPtZ+7H893z0X/6+LroncahjqvfJsFXafCzy5VLVIqL5OsrMWc1L8babedIuFFX42zHzLkPyiGt4uA==",
577
614
  "license": "Apache-2.0",
578
615
  "optional": true,
579
616
  "dependencies": {
@@ -633,16 +670,16 @@
633
670
  }
634
671
  },
635
672
  "node_modules/appium-remote-debugger": {
636
- "version": "15.9.1",
637
- "resolved": "https://registry.npmjs.org/appium-remote-debugger/-/appium-remote-debugger-15.9.1.tgz",
638
- "integrity": "sha512-+ms2NsTfu+L1JN9UbFUQo4ZiJV1FFgcm3PSWRgJziI7TasOtShYgcNt2d+V9o4fkf9+BESOAhQ6Fm0FVNdISLA==",
673
+ "version": "15.10.1",
674
+ "resolved": "https://registry.npmjs.org/appium-remote-debugger/-/appium-remote-debugger-15.10.1.tgz",
675
+ "integrity": "sha512-IcEXCuUNUIZVmOLiC6gmpBarh4ZM1ESQkSLD8Yb6v3oC6ksUHUFPRDdpdH48G38AgseGSnTFD15RSv4FqWwasw==",
639
676
  "license": "Apache-2.0",
640
677
  "dependencies": {
641
678
  "@appium/base-driver": "^10.0.0-rc.1",
642
- "@appium/support": "^7.0.0-rc.1",
679
+ "@appium/support": "^7.2.2",
643
680
  "appium-ios-device": "^3.0.0",
644
681
  "async-lock": "^1.4.1",
645
- "asyncbox": "^6.1.0",
682
+ "asyncbox": "^6.3.0",
646
683
  "glob": "^13.0.0",
647
684
  "teen_process": "^4.0.4"
648
685
  },
@@ -655,9 +692,9 @@
655
692
  }
656
693
  },
657
694
  "node_modules/appium-webdriveragent": {
658
- "version": "12.2.1",
659
- "resolved": "https://registry.npmjs.org/appium-webdriveragent/-/appium-webdriveragent-12.2.1.tgz",
660
- "integrity": "sha512-iSY3s4EUiNqY7SZg36ufI3Jt84dBDcFxIUxmusAUJzjxkHPYy+VNTOyIdtuP5RkhFNoSh4VHg8JjusGKqdKWwg==",
695
+ "version": "12.2.2",
696
+ "resolved": "https://registry.npmjs.org/appium-webdriveragent/-/appium-webdriveragent-12.2.2.tgz",
697
+ "integrity": "sha512-/GbzAjEHaaNgBUK8eVnmtf8s2fudjyCi55IBoEzv0sFg/188Bo+wsJfQhECjrk9CdhkpiwjsHAlJGLUy+iEpIQ==",
661
698
  "license": "Apache-2.0",
662
699
  "dependencies": {
663
700
  "@appium/base-driver": "^10.3.0",
@@ -667,7 +704,7 @@
667
704
  "appium-ios-simulator": "^8.0.0",
668
705
  "async-lock": "^1.0.0",
669
706
  "asyncbox": "^6.1.0",
670
- "axios": "^1.4.0",
707
+ "axios": "^1.16.0",
671
708
  "teen_process": "^4.0.7"
672
709
  },
673
710
  "engines": {
@@ -820,9 +857,9 @@
820
857
  "license": "MIT"
821
858
  },
822
859
  "node_modules/asyncbox": {
823
- "version": "6.2.0",
824
- "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.2.0.tgz",
825
- "integrity": "sha512-z1XpHkoT3y+1aXfazEY5d7HN2eOi50fLq7ZTxG0H4WegLxrtEAI5Vsc6OR9dOwoC3SJQLXyV0ZVnPEh6GIgMKQ==",
860
+ "version": "6.3.0",
861
+ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.0.tgz",
862
+ "integrity": "sha512-7IFpnQDltd5rYQjhIJIpyismJtdWmw/pOABZKJfv2WVo0a6iYh2ZzUuCJJclae5mBtK0H/EychxXg91GB7rGdQ==",
826
863
  "license": "Apache-2.0",
827
864
  "dependencies": {
828
865
  "p-limit": "^7.2.0"
@@ -3450,9 +3487,9 @@
3450
3487
  "optional": true
3451
3488
  },
3452
3489
  "node_modules/semver": {
3453
- "version": "7.7.4",
3454
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
3455
- "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
3490
+ "version": "7.8.0",
3491
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
3492
+ "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
3456
3493
  "license": "ISC",
3457
3494
  "bin": {
3458
3495
  "semver": "bin/semver.js"
@@ -4398,9 +4435,9 @@
4398
4435
  "license": "ISC"
4399
4436
  },
4400
4437
  "node_modules/ws": {
4401
- "version": "8.20.0",
4402
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
4403
- "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
4438
+ "version": "8.20.1",
4439
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz",
4440
+ "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==",
4404
4441
  "license": "MIT",
4405
4442
  "engines": {
4406
4443
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "xcuitest",
9
9
  "xctest"
10
10
  ],
11
- "version": "11.2.4",
11
+ "version": "11.3.1",
12
12
  "author": "Appium Contributors",
13
13
  "license": "Apache-2.0",
14
14
  "repository": {
@@ -34,6 +34,7 @@
34
34
  "build-wda": "./scripts/build-wda.mjs",
35
35
  "open-wda": "./scripts/open-wda.mjs",
36
36
  "tunnel-creation": "./scripts/tunnel-creation.mjs",
37
+ "download-wda": "./scripts/download-wda.mjs",
37
38
  "download-wda-sim": "./scripts/download-wda-sim.mjs",
38
39
  "image-mounter": "./scripts/image-mounter.mjs",
39
40
  "list-real-devices": "./scripts/list-real-devices.mjs",
@@ -106,7 +107,7 @@
106
107
  "ws": "^8.13.0"
107
108
  },
108
109
  "optionalDependencies": {
109
- "appium-ios-remotexpc": "^1.1.6"
110
+ "appium-ios-remotexpc": "^1.1.8"
110
111
  },
111
112
  "scripts": {
112
113
  "build": "tsc -b",
@@ -1,80 +1,25 @@
1
- import {fs, logger, zip, net, node} from 'appium/support.js';
2
- import _ from 'lodash';
3
- import os from 'node:os';
4
- import path from 'node:path';
1
+ import {getWDAPrebuiltPackage} from './download-wda.mjs';
5
2
  import {Command} from 'commander';
3
+ import { deprecate } from 'node:util';
6
4
 
7
- const log = logger.getLogger('download-wda-sim');
8
- const wdaUrl = (/** @type {string} */ version, /** @type {string} */ zipFileName) =>
9
- `https://github.com/appium/WebDriverAgent/releases/download/v${version}/${zipFileName}`;
10
- const destZip = (/** @type {string} */ platform) => {
11
- const scheme = `WebDriverAgentRunner${_.toLower(platform) === 'tvos' ? '_tvOS' : ''}`;
12
- return `${scheme}-Build-Sim-${os.arch() === 'arm64' ? 'arm64' : 'x86_64'}.zip`;
13
- };
14
-
15
- /**
16
- * Return installed appium-webdriveragent package version
17
- * @returns {Promise<string>}
18
- */
19
- async function webdriveragentPkgVersion() {
20
- const moduleRoot = node.getModuleRootSync('appium-xcuitest-driver', import.meta.url);
21
- if (!moduleRoot) {
22
- throw new Error('Cannot resolve module root for appium-xcuitest-driver');
23
- }
24
- const pkgPath = path.join(
25
- moduleRoot,
26
- 'node_modules',
27
- 'appium-webdriveragent',
28
- 'package.json'
29
- );
30
- const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
31
- return String(pkg.version);
32
- };
33
-
34
- /**
35
- * Prepare the working root directory.
36
- * @param {string} outdir
37
- * @returns {Promise<string>} Root directory to download and unzip.
38
- */
39
- async function prepareRootDir(outdir) {
40
- const destDir = path.resolve(process.cwd(), outdir);
41
- if (await fs.exists(destDir)) {
42
- throw new Error(`${destDir} already exists`);
43
- }
44
- await fs.mkdir(destDir, {recursive: true});
45
- return destDir;
46
- }
47
-
48
- /**
49
- * @param {DownloadOptions} options
50
- */
51
- async function getWDAPrebuiltPackage(options) {
52
- const destDir = await prepareRootDir(options.outdir);
53
- const zipFileName = destZip(options.platform);
54
- const wdaVersion = await webdriveragentPkgVersion();
55
- const urlToDownload = wdaUrl(wdaVersion, zipFileName);
56
- const downloadedZipFile = path.join(destDir, zipFileName);
57
- try {
58
- log.info(`Downloading ${urlToDownload}`);
59
- await net.downloadFile(urlToDownload, downloadedZipFile);
60
-
61
- log.info(`Unpacking ${downloadedZipFile} into ${destDir}`);
62
- await zip.extractAllTo(downloadedZipFile, destDir);
63
-
64
- log.info(`Deleting ${downloadedZipFile}`);
65
- } finally {
66
- if (await fs.exists(downloadedZipFile)) {
67
- await fs.unlink(downloadedZipFile);
68
- }
69
- }
70
- }
5
+ const DEPRECATION_MESSAGE =
6
+ "[DEPRECATED] 'download-wda-sim' is deprecated. " +
7
+ "Use 'appium driver run xcuitest download-wda -- --kind=sim --platform=<platform> --outdir=<outdir>' instead.";
71
8
 
72
9
  async function main() {
73
10
  const program = new Command();
74
11
 
12
+ const oldHandler = deprecate(
13
+ async (options) => {
14
+ await getWDAPrebuiltPackage({...options, kind: 'sim'});
15
+ },
16
+ DEPRECATION_MESSAGE
17
+ );
18
+
75
19
  program
76
20
  .name('appium driver run xcuitest download-wda-sim')
77
21
  .description('Download a prebuilt WebDriverAgentRunner for iOS/tvOS simulator')
22
+ .addHelpText('beforeAll', `${DEPRECATION_MESSAGE}\n\n`)
78
23
  .requiredOption('--outdir <path>', 'Destination directory to download and unpack into')
79
24
  .requiredOption(
80
25
  '--platform <platform>',
@@ -86,14 +31,12 @@ async function main() {
86
31
  `
87
32
  EXAMPLES:
88
33
  # Download WDA for iOS simulator
89
- appium driver run xcuitest download-wda-sim --outdir ./wda-sim --platform iOS
34
+ appium driver run xcuitest download-wda-sim -- --outdir ./wda-sim --platform iOS
90
35
 
91
36
  # Download WDA for tvOS simulator
92
- appium driver run xcuitest download-wda-sim --outdir ./wda-sim-tvos --platform tvOS`,
37
+ appium driver run xcuitest download-wda-sim -- --outdir ./wda-sim-tvos --platform tvOS`,
93
38
  )
94
- .action(async (options) => {
95
- await getWDAPrebuiltPackage(options);
96
- });
39
+ .action(oldHandler);
97
40
 
98
41
  await program.parseAsync(process.argv);
99
42
  }
@@ -0,0 +1,167 @@
1
+ import {fs, logger, zip, net, node} from 'appium/support.js';
2
+ import {constants as fsConstants, promises as fsPromises} from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import {pathToFileURL} from 'node:url';
6
+ import {Command} from 'commander';
7
+
8
+ const log = logger.getLogger('download-wda');
9
+ const WDA_KIND_REAL = 'real';
10
+ const WDA_KIND_SIM = 'sim';
11
+
12
+ const wdaUrl = (/** @type {string} */ version, /** @type {string} */ zipFileName) =>
13
+ `https://github.com/appium/WebDriverAgent/releases/download/v${version}/${zipFileName}`;
14
+
15
+ /**
16
+ * Download and unpack a prebuilt WDA package for the given platform and kind.
17
+ * @param {DownloadOptions} options
18
+ * @returns {Promise<void>}
19
+ */
20
+ export async function getWDAPrebuiltPackage(options) {
21
+ const kind = normalizeKind(options.kind);
22
+ const destDir = await prepareRootDir(options.outdir);
23
+ const zipFileName = destZip(options.platform, kind);
24
+ const wdaVersion = await getWebdriveragentPkgVersion();
25
+ const urlToDownload = wdaUrl(wdaVersion, zipFileName);
26
+ const downloadedZipFile = path.join(destDir, zipFileName);
27
+ try {
28
+ log.info(`Downloading ${urlToDownload}`);
29
+ await net.downloadFile(urlToDownload, downloadedZipFile);
30
+
31
+ log.info(`Unpacking ${downloadedZipFile} into ${destDir}`);
32
+ await zip.extractAllTo(downloadedZipFile, destDir);
33
+
34
+ log.info(`Deleting ${downloadedZipFile}`);
35
+ } finally {
36
+ if (await fs.exists(downloadedZipFile)) {
37
+ await fs.unlink(downloadedZipFile);
38
+ }
39
+ }
40
+ }
41
+
42
+ const destZip = (/** @type {string} */ platform, /** @type {WDAKind} */ kind) => {
43
+ const scheme = `WebDriverAgentRunner${String(platform).toLowerCase() === 'tvos' ? '_tvOS' : ''}`;
44
+ if (kind === WDA_KIND_SIM) {
45
+ return `${scheme}-Build-Sim-${os.arch() === 'arm64' ? 'arm64' : 'x86_64'}.zip`;
46
+ }
47
+ return `${scheme}-Runner.zip`;
48
+ };
49
+
50
+ /**
51
+ * Normalize the kind value, ensuring it is either 'real' or 'sim'. Default to 'real' if undefined.
52
+ * @param {string | undefined} kind
53
+ * @returns {WDAKind}
54
+ */
55
+ function normalizeKind(kind) {
56
+ const normalized = String(kind || WDA_KIND_REAL).toLowerCase();
57
+ if (![WDA_KIND_REAL, WDA_KIND_SIM].includes(normalized)) {
58
+ throw new Error(`Unsupported kind '${kind}'. Supported values are '${WDA_KIND_REAL}' and '${WDA_KIND_SIM}'`);
59
+ }
60
+ return /** @type {WDAKind} */ (normalized);
61
+ }
62
+
63
+ /**
64
+ * Return installed appium-webdriveragent package version
65
+ * @returns {Promise<string>}
66
+ */
67
+ async function getWebdriveragentPkgVersion() {
68
+ const moduleRoot = node.getModuleRootSync('appium-xcuitest-driver', import.meta.url);
69
+ if (!moduleRoot) {
70
+ throw new Error('Cannot resolve module root for appium-xcuitest-driver');
71
+ }
72
+ const pkgPath = path.join(
73
+ moduleRoot,
74
+ 'node_modules',
75
+ 'appium-webdriveragent',
76
+ 'package.json'
77
+ );
78
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
79
+ if (!pkg.version || typeof pkg.version !== 'string') {
80
+ throw new Error(`Cannot find version in ${pkgPath}`);
81
+ }
82
+ return pkg.version;
83
+ };
84
+
85
+ /**
86
+ * Prepare the working root directory.
87
+ * @param {string} outdir
88
+ * @returns {Promise<string>} Root directory to download and unzip.
89
+ */
90
+ async function prepareRootDir(outdir) {
91
+ const destDir = path.resolve(process.cwd(), outdir);
92
+ if (await fs.exists(destDir)) {
93
+ throw new Error(`${destDir} already exists`);
94
+ }
95
+
96
+ const parentDir = path.dirname(destDir);
97
+ try {
98
+ await fsPromises.access(parentDir, fsConstants.W_OK);
99
+ } catch (err) {
100
+ throw new Error(`Parent directory '${parentDir}' is not writable`, {
101
+ cause: err,
102
+ });
103
+ }
104
+
105
+ try {
106
+ await fs.mkdir(destDir, {recursive: true});
107
+ } catch (err) {
108
+ const message = err instanceof Error ? err.message : String(err);
109
+ throw new Error(`Cannot create directory '${destDir}': ${message}`, {
110
+ cause: err,
111
+ });
112
+ }
113
+ return destDir;
114
+ }
115
+
116
+ async function main() {
117
+ const program = new Command();
118
+
119
+ program
120
+ .name('appium driver run xcuitest download-wda')
121
+ .description('Download a prebuilt WebDriverAgentRunner for iOS/tvOS real devices or simulators')
122
+ .requiredOption('--outdir <path>', 'Destination directory to download and unpack into')
123
+ .requiredOption(
124
+ '--platform <platform>',
125
+ 'Target platform (e.g. iOS or tvOS)',
126
+ (value) => value,
127
+ )
128
+ .option(
129
+ '--kind <kind>',
130
+ `Target package type: ${WDA_KIND_REAL} (real devices) or ${WDA_KIND_SIM} (simulators). Default: ${WDA_KIND_REAL}`,
131
+ )
132
+ .addHelpText(
133
+ 'after',
134
+ `
135
+ EXAMPLES:
136
+ # Download WDA for iOS real device (default)
137
+ appium driver run xcuitest download-wda -- --outdir ./wda-real --platform iOS
138
+
139
+ # Download WDA for tvOS simulator
140
+ appium driver run xcuitest download-wda -- --outdir ./wda-sim-tvos --platform tvOS --kind sim`,
141
+ )
142
+ .action(async (options) => {
143
+ await getWDAPrebuiltPackage({
144
+ ...options,
145
+ kind: options.kind ?? WDA_KIND_REAL,
146
+ });
147
+ });
148
+
149
+ await program.parseAsync(process.argv);
150
+ }
151
+
152
+ const isMainModule =
153
+ Boolean(process.argv[1]) && import.meta.url === pathToFileURL(process.argv[1]).href;
154
+ if (isMainModule) {
155
+ await main();
156
+ }
157
+
158
+ /**
159
+ * @typedef {'real' | 'sim'} WDAKind
160
+ */
161
+
162
+ /**
163
+ * @typedef {Object} DownloadOptions
164
+ * @property {string} outdir
165
+ * @property {string} platform
166
+ * @property {WDAKind | undefined} [kind]
167
+ */