@debugswift/ios-simulator-cli 1.6.0 → 1.7.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 +5 -2
- package/build/index.js +372 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,14 +10,17 @@ A command-line tool for interacting with iOS simulators. Control UI, capture scr
|
|
|
10
10
|
|
|
11
11
|
### Homebrew (recommended)
|
|
12
12
|
|
|
13
|
+
The formula lives in this repo, so tap it explicitly (Homebrew otherwise looks for a separate `homebrew-ios-simulator-cli` repo that does not exist):
|
|
14
|
+
|
|
13
15
|
```bash
|
|
14
|
-
brew
|
|
16
|
+
brew tap DebugSwift/ios-simulator-cli https://github.com/DebugSwift/ios-simulator-cli
|
|
17
|
+
brew install ios-simulator-cli
|
|
15
18
|
```
|
|
16
19
|
|
|
17
20
|
To install the latest from `main`:
|
|
18
21
|
|
|
19
22
|
```bash
|
|
20
|
-
brew install --HEAD
|
|
23
|
+
brew install --HEAD ios-simulator-cli
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
### npm
|
package/build/index.js
CHANGED
|
@@ -62,8 +62,46 @@ function getIdbPath() {
|
|
|
62
62
|
}
|
|
63
63
|
return expandedPath;
|
|
64
64
|
}
|
|
65
|
+
const pythonBins = [];
|
|
66
|
+
const pythonRoot = path_1.default.join(os_1.default.homedir(), "Library/Python");
|
|
67
|
+
if (fs_1.default.existsSync(pythonRoot)) {
|
|
68
|
+
for (const entry of fs_1.default.readdirSync(pythonRoot, { withFileTypes: true })) {
|
|
69
|
+
if (!entry.isDirectory())
|
|
70
|
+
continue;
|
|
71
|
+
pythonBins.push(path_1.default.join(pythonRoot, entry.name, "bin/idb"));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const candidates = preferWorkingIdb([
|
|
75
|
+
path_1.default.join(os_1.default.homedir(), ".local/bin/idb"),
|
|
76
|
+
...pythonBins,
|
|
77
|
+
"/opt/homebrew/bin/idb",
|
|
78
|
+
"/usr/local/bin/idb",
|
|
79
|
+
].filter((candidate) => fs_1.default.existsSync(candidate)));
|
|
80
|
+
if (candidates.length > 0) {
|
|
81
|
+
return candidates[0];
|
|
82
|
+
}
|
|
65
83
|
return "idb";
|
|
66
84
|
}
|
|
85
|
+
/** Prefer arm64-friendly idb installs (Xcode/system Python) over Intel Homebrew Python. */
|
|
86
|
+
function preferWorkingIdb(candidates) {
|
|
87
|
+
const score = (idbPath) => {
|
|
88
|
+
try {
|
|
89
|
+
const shebang = fs_1.default.readFileSync(idbPath, "utf8").split("\n", 1)[0] ?? "";
|
|
90
|
+
if (shebang.includes("/usr/local/opt/python"))
|
|
91
|
+
return 2;
|
|
92
|
+
if (shebang.includes("Xcode.app") ||
|
|
93
|
+
shebang.includes("/usr/bin/python") ||
|
|
94
|
+
shebang.includes("/opt/homebrew/")) {
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return [...candidates].sort((a, b) => score(a) - score(b));
|
|
104
|
+
}
|
|
67
105
|
async function idb(...args) {
|
|
68
106
|
return run(getIdbPath(), args);
|
|
69
107
|
}
|
|
@@ -145,6 +183,61 @@ function parseEnvPairs(values) {
|
|
|
145
183
|
}
|
|
146
184
|
return env;
|
|
147
185
|
}
|
|
186
|
+
function matchesSearch(value, term, mode, sensitive) {
|
|
187
|
+
if (value == null)
|
|
188
|
+
return false;
|
|
189
|
+
const v = sensitive ? value : value.toLowerCase();
|
|
190
|
+
const t = sensitive ? term : term.toLowerCase();
|
|
191
|
+
return mode === "exact" ? v === t : v.includes(t);
|
|
192
|
+
}
|
|
193
|
+
function findUiElements(elements, options) {
|
|
194
|
+
const results = [];
|
|
195
|
+
for (const element of elements) {
|
|
196
|
+
const label = element.AXLabel;
|
|
197
|
+
const uniqueId = element.AXUniqueId;
|
|
198
|
+
const elementType = element.type;
|
|
199
|
+
const matchesAnySearch = options.search.some((term) => matchesSearch(label, term, options.matchMode, options.caseSensitive) ||
|
|
200
|
+
matchesSearch(uniqueId, term, options.matchMode, options.caseSensitive));
|
|
201
|
+
const matchesType = options.type == null ||
|
|
202
|
+
(elementType != null &&
|
|
203
|
+
elementType.toLowerCase() === options.type.toLowerCase());
|
|
204
|
+
if (matchesAnySearch && matchesType) {
|
|
205
|
+
results.push(element);
|
|
206
|
+
}
|
|
207
|
+
const children = element.children;
|
|
208
|
+
if (children && children.length > 0) {
|
|
209
|
+
results.push(...findUiElements(children, options));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return results;
|
|
213
|
+
}
|
|
214
|
+
async function fetchUiTree(udid) {
|
|
215
|
+
const actualUdid = await getBootedDeviceId(udidSchema.parse(udid));
|
|
216
|
+
const { stdout } = await idb("ui", "describe-all", "--udid", actualUdid, "--json", "--nested");
|
|
217
|
+
return JSON.parse(stdout);
|
|
218
|
+
}
|
|
219
|
+
function elementCenter(element) {
|
|
220
|
+
const frame = element.frame;
|
|
221
|
+
if (!frame ||
|
|
222
|
+
typeof frame.x !== "number" ||
|
|
223
|
+
typeof frame.y !== "number" ||
|
|
224
|
+
typeof frame.width !== "number" ||
|
|
225
|
+
typeof frame.height !== "number") {
|
|
226
|
+
throw new Error("Element has no valid frame for tapping");
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
x: Math.round(frame.x + frame.width / 2),
|
|
230
|
+
y: Math.round(frame.y + frame.height / 2),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function sleep(ms) {
|
|
234
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
235
|
+
}
|
|
236
|
+
const matchModeSchema = zod_1.z.enum(["substring", "exact"]);
|
|
237
|
+
const workflowSchema = zod_1.z.object({
|
|
238
|
+
udid: udidSchema,
|
|
239
|
+
steps: zod_1.z.array(zod_1.z.record(zod_1.z.unknown())).min(1),
|
|
240
|
+
});
|
|
148
241
|
async function cmdGetBootedSimId() {
|
|
149
242
|
const { id, name } = await getBootedDevice();
|
|
150
243
|
console.log(`Booted Simulator: "${name}". UUID: "${id}"`);
|
|
@@ -188,38 +281,8 @@ async function cmdUiDescribePoint(options) {
|
|
|
188
281
|
console.log(stdout);
|
|
189
282
|
}
|
|
190
283
|
async function cmdUiFindElement(options) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const uiData = JSON.parse(stdout);
|
|
194
|
-
function matchesSearch(value, term, mode, sensitive) {
|
|
195
|
-
if (value == null)
|
|
196
|
-
return false;
|
|
197
|
-
const v = sensitive ? value : value.toLowerCase();
|
|
198
|
-
const t = sensitive ? term : term.toLowerCase();
|
|
199
|
-
return mode === "exact" ? v === t : v.includes(t);
|
|
200
|
-
}
|
|
201
|
-
function findElements(elements) {
|
|
202
|
-
const results = [];
|
|
203
|
-
for (const element of elements) {
|
|
204
|
-
const label = element.AXLabel;
|
|
205
|
-
const uniqueId = element.AXUniqueId;
|
|
206
|
-
const elementType = element.type;
|
|
207
|
-
const matchesAnySearch = options.search.some((term) => matchesSearch(label, term, options.matchMode, options.caseSensitive) ||
|
|
208
|
-
matchesSearch(uniqueId, term, options.matchMode, options.caseSensitive));
|
|
209
|
-
const matchesType = options.type == null ||
|
|
210
|
-
(elementType != null &&
|
|
211
|
-
elementType.toLowerCase() === options.type.toLowerCase());
|
|
212
|
-
if (matchesAnySearch && matchesType) {
|
|
213
|
-
results.push(element);
|
|
214
|
-
}
|
|
215
|
-
const children = element.children;
|
|
216
|
-
if (children && children.length > 0) {
|
|
217
|
-
results.push(...findElements(children));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return results;
|
|
221
|
-
}
|
|
222
|
-
console.log(JSON.stringify(findElements(uiData)));
|
|
284
|
+
const uiData = await fetchUiTree(options.udid);
|
|
285
|
+
console.log(JSON.stringify(findUiElements(uiData, options)));
|
|
223
286
|
}
|
|
224
287
|
async function cmdUiView(options) {
|
|
225
288
|
const actualUdid = await getBootedDeviceId(udidSchema.parse(options.udid));
|
|
@@ -383,6 +446,240 @@ async function cmdLaunchApp(options) {
|
|
|
383
446
|
? `App ${options.bundleId} launched successfully with PID: ${pid}`
|
|
384
447
|
: `App ${options.bundleId} launched successfully`);
|
|
385
448
|
}
|
|
449
|
+
function stepUdid(globalUdid, step) {
|
|
450
|
+
const value = step.udid;
|
|
451
|
+
if (value == null)
|
|
452
|
+
return globalUdid;
|
|
453
|
+
return udidSchema.parse(value);
|
|
454
|
+
}
|
|
455
|
+
function parseStepObject(step, index) {
|
|
456
|
+
if (typeof step !== "object" || step == null || Array.isArray(step)) {
|
|
457
|
+
throw new Error(`Step ${index + 1}: expected an object`);
|
|
458
|
+
}
|
|
459
|
+
const keys = Object.keys(step);
|
|
460
|
+
if (keys.length !== 1) {
|
|
461
|
+
throw new Error(`Step ${index + 1}: expected exactly one action, got: ${keys.join(", ") || "(empty)"}`);
|
|
462
|
+
}
|
|
463
|
+
return step;
|
|
464
|
+
}
|
|
465
|
+
async function runWorkflowStep(action, payload, globalUdid, index) {
|
|
466
|
+
const stepLabel = `Step ${index + 1}: ${action}`;
|
|
467
|
+
console.log(stepLabel);
|
|
468
|
+
switch (action) {
|
|
469
|
+
case "wait": {
|
|
470
|
+
const ms = zod_1.z.number().int().nonnegative().parse(payload);
|
|
471
|
+
await sleep(ms);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
case "open": {
|
|
475
|
+
if (payload != null && payload !== true) {
|
|
476
|
+
throw new Error(`${stepLabel} expects true or no value`);
|
|
477
|
+
}
|
|
478
|
+
await cmdOpenSimulator();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
case "launch-app": {
|
|
482
|
+
const step = zod_1.z
|
|
483
|
+
.object({
|
|
484
|
+
bundleId: zod_1.z.string().min(1),
|
|
485
|
+
terminateRunning: zod_1.z.boolean().optional(),
|
|
486
|
+
env: zod_1.z.record(zod_1.z.string()).optional(),
|
|
487
|
+
udid: udidSchema,
|
|
488
|
+
})
|
|
489
|
+
.parse(payload);
|
|
490
|
+
await cmdLaunchApp({
|
|
491
|
+
udid: stepUdid(globalUdid, step),
|
|
492
|
+
bundleId: step.bundleId,
|
|
493
|
+
terminateRunning: step.terminateRunning,
|
|
494
|
+
env: step.env,
|
|
495
|
+
});
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
case "install-app": {
|
|
499
|
+
const step = zod_1.z
|
|
500
|
+
.object({
|
|
501
|
+
appPath: zod_1.z.string().min(1),
|
|
502
|
+
udid: udidSchema,
|
|
503
|
+
})
|
|
504
|
+
.parse(payload);
|
|
505
|
+
await cmdInstallApp(step.appPath, stepUdid(globalUdid, step));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
case "tap": {
|
|
509
|
+
const step = zod_1.z
|
|
510
|
+
.object({
|
|
511
|
+
x: zod_1.z.number().optional(),
|
|
512
|
+
y: zod_1.z.number().optional(),
|
|
513
|
+
search: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional(),
|
|
514
|
+
type: zod_1.z.string().optional(),
|
|
515
|
+
matchMode: matchModeSchema.optional(),
|
|
516
|
+
caseSensitive: zod_1.z.boolean().optional(),
|
|
517
|
+
index: zod_1.z.number().int().nonnegative().optional(),
|
|
518
|
+
duration: zod_1.z.string().regex(/^\d+(\.\d+)?$/).optional(),
|
|
519
|
+
udid: udidSchema,
|
|
520
|
+
})
|
|
521
|
+
.parse(payload);
|
|
522
|
+
const udid = stepUdid(globalUdid, step);
|
|
523
|
+
if (step.x != null && step.y != null) {
|
|
524
|
+
await cmdUiTap({
|
|
525
|
+
udid,
|
|
526
|
+
x: step.x,
|
|
527
|
+
y: step.y,
|
|
528
|
+
duration: step.duration,
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (step.search == null) {
|
|
533
|
+
throw new Error(`${stepLabel} requires x/y or search`);
|
|
534
|
+
}
|
|
535
|
+
const search = Array.isArray(step.search) ? step.search : [step.search];
|
|
536
|
+
const matches = findUiElements(await fetchUiTree(udid), {
|
|
537
|
+
search,
|
|
538
|
+
type: step.type,
|
|
539
|
+
matchMode: step.matchMode ?? "substring",
|
|
540
|
+
caseSensitive: step.caseSensitive ?? false,
|
|
541
|
+
});
|
|
542
|
+
const matchIndex = step.index ?? 0;
|
|
543
|
+
if (matches.length === 0) {
|
|
544
|
+
throw new Error(`${stepLabel} found no element matching: ${search.join(", ")}`);
|
|
545
|
+
}
|
|
546
|
+
if (matchIndex >= matches.length) {
|
|
547
|
+
throw new Error(`${stepLabel} index ${matchIndex} out of range (${matches.length} matches)`);
|
|
548
|
+
}
|
|
549
|
+
const { x, y } = elementCenter(matches[matchIndex]);
|
|
550
|
+
await cmdUiTap({ udid, x, y, duration: step.duration });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
case "type": {
|
|
554
|
+
const step = zod_1.z
|
|
555
|
+
.object({
|
|
556
|
+
text: zod_1.z.string().min(1).max(500).regex(/^[\x20-\x7E]+$/),
|
|
557
|
+
udid: udidSchema,
|
|
558
|
+
})
|
|
559
|
+
.parse(payload);
|
|
560
|
+
await cmdUiType(step.text, stepUdid(globalUdid, step));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
case "swipe": {
|
|
564
|
+
const step = zod_1.z
|
|
565
|
+
.object({
|
|
566
|
+
xStart: zod_1.z.number(),
|
|
567
|
+
yStart: zod_1.z.number(),
|
|
568
|
+
xEnd: zod_1.z.number(),
|
|
569
|
+
yEnd: zod_1.z.number(),
|
|
570
|
+
duration: zod_1.z.string().regex(/^\d+(\.\d+)?$/).optional(),
|
|
571
|
+
delta: zod_1.z.number().optional(),
|
|
572
|
+
udid: udidSchema,
|
|
573
|
+
})
|
|
574
|
+
.parse(payload);
|
|
575
|
+
await cmdUiSwipe({
|
|
576
|
+
udid: stepUdid(globalUdid, step),
|
|
577
|
+
xStart: step.xStart,
|
|
578
|
+
yStart: step.yStart,
|
|
579
|
+
xEnd: step.xEnd,
|
|
580
|
+
yEnd: step.yEnd,
|
|
581
|
+
duration: step.duration,
|
|
582
|
+
delta: step.delta,
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
case "screenshot": {
|
|
587
|
+
const step = zod_1.z
|
|
588
|
+
.object({
|
|
589
|
+
output: zod_1.z.string().min(1),
|
|
590
|
+
type: zod_1.z.enum(["png", "tiff", "bmp", "gif", "jpeg"]).optional(),
|
|
591
|
+
display: zod_1.z.enum(["internal", "external"]).optional(),
|
|
592
|
+
mask: zod_1.z.enum(["ignored", "alpha", "black"]).optional(),
|
|
593
|
+
udid: udidSchema,
|
|
594
|
+
})
|
|
595
|
+
.parse(payload);
|
|
596
|
+
await cmdScreenshot({
|
|
597
|
+
udid: stepUdid(globalUdid, step),
|
|
598
|
+
outputPath: step.output,
|
|
599
|
+
type: step.type,
|
|
600
|
+
display: step.display,
|
|
601
|
+
mask: step.mask,
|
|
602
|
+
});
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
case "ui-view": {
|
|
606
|
+
const step = zod_1.z
|
|
607
|
+
.object({
|
|
608
|
+
output: zod_1.z.string().optional(),
|
|
609
|
+
udid: udidSchema,
|
|
610
|
+
})
|
|
611
|
+
.parse(payload);
|
|
612
|
+
await cmdUiView({
|
|
613
|
+
udid: stepUdid(globalUdid, step),
|
|
614
|
+
output: step.output,
|
|
615
|
+
});
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
default:
|
|
619
|
+
throw new Error(`${stepLabel} uses unknown action "${action}". Run ios-simulator-cli run --help for supported actions.`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async function cmdRun(configPath) {
|
|
623
|
+
const absolutePath = path_1.default.isAbsolute(configPath)
|
|
624
|
+
? configPath
|
|
625
|
+
: path_1.default.resolve(configPath);
|
|
626
|
+
if (!fs_1.default.existsSync(absolutePath)) {
|
|
627
|
+
throw new Error(`Config file not found: ${absolutePath}`);
|
|
628
|
+
}
|
|
629
|
+
let parsed;
|
|
630
|
+
try {
|
|
631
|
+
parsed = JSON.parse(fs_1.default.readFileSync(absolutePath, "utf8"));
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
throw new Error(`Invalid JSON in config file: ${absolutePath}`);
|
|
635
|
+
}
|
|
636
|
+
const workflow = workflowSchema.parse(parsed);
|
|
637
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
638
|
+
const step = parseStepObject(workflow.steps[i], i);
|
|
639
|
+
const [action, payload] = Object.entries(step)[0];
|
|
640
|
+
await runWorkflowStep(action, payload, workflow.udid, i);
|
|
641
|
+
}
|
|
642
|
+
console.log(`Completed ${workflow.steps.length} step(s)`);
|
|
643
|
+
}
|
|
644
|
+
function runHelpBody() {
|
|
645
|
+
return `Run a JSON workflow file against the booted simulator.
|
|
646
|
+
|
|
647
|
+
Config format:
|
|
648
|
+
{
|
|
649
|
+
"udid": "<optional-simulator-uuid>",
|
|
650
|
+
"steps": [
|
|
651
|
+
{ "open": true },
|
|
652
|
+
{ "wait": 1000 },
|
|
653
|
+
{ "launch-app": { "bundleId": "com.example.app", "terminateRunning": true } },
|
|
654
|
+
{ "tap": { "search": "Sign In", "type": "Button" } },
|
|
655
|
+
{ "tap": { "x": 200, "y": 400 } },
|
|
656
|
+
{ "type": { "text": "hello@world.com" } },
|
|
657
|
+
{ "swipe": { "xStart": 200, "yStart": 600, "xEnd": 200, "yEnd": 200 } },
|
|
658
|
+
{ "screenshot": { "output": "result.png" } }
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
Supported step actions:
|
|
663
|
+
wait Milliseconds to pause (number)
|
|
664
|
+
open Open Simulator.app (true)
|
|
665
|
+
launch-app Launch app by bundle ID
|
|
666
|
+
install-app Install .app or .ipa
|
|
667
|
+
tap Tap by x/y or by accessibility search label
|
|
668
|
+
type Type ASCII text
|
|
669
|
+
swipe Swipe gesture
|
|
670
|
+
screenshot Save screenshot to file
|
|
671
|
+
ui-view Capture compressed JPEG view
|
|
672
|
+
|
|
673
|
+
Each step object must contain exactly one action key.
|
|
674
|
+
Step payloads may include an optional "udid" to override the config-level udid.`;
|
|
675
|
+
}
|
|
676
|
+
function printRunHelp() {
|
|
677
|
+
console.log(`ios-simulator-cli run --config <path>
|
|
678
|
+
ios-simulator-cli --config <path>
|
|
679
|
+
|
|
680
|
+
${runHelpBody()}
|
|
681
|
+
`);
|
|
682
|
+
}
|
|
386
683
|
function printHelp() {
|
|
387
684
|
console.log(`ios-simulator-cli v${VERSION}
|
|
388
685
|
|
|
@@ -406,6 +703,8 @@ Commands:
|
|
|
406
703
|
stop-recording Stop an active simulator recording
|
|
407
704
|
install-app --app-path <path> [--udid <uuid>]
|
|
408
705
|
launch-app --bundle-id <id> [--terminate-running] [--env KEY=VALUE] [--udid <uuid>]
|
|
706
|
+
run --config <path> Run a JSON workflow file
|
|
707
|
+
--config <path> Shorthand for run --config
|
|
409
708
|
|
|
410
709
|
Global options:
|
|
411
710
|
-h, --help Show this help
|
|
@@ -420,6 +719,10 @@ Examples:
|
|
|
420
719
|
ios-simulator-cli ui tap --x 200 --y 400
|
|
421
720
|
ios-simulator-cli screenshot --output home.png
|
|
422
721
|
ios-simulator-cli launch-app --bundle-id com.apple.mobilesafari
|
|
722
|
+
ios-simulator-cli run --config flow.json
|
|
723
|
+
ios-simulator-cli --config flow.json
|
|
724
|
+
|
|
725
|
+
${runHelpBody()}
|
|
423
726
|
`);
|
|
424
727
|
}
|
|
425
728
|
async function handleUiCommand(args) {
|
|
@@ -564,7 +867,15 @@ async function handleUiCommand(args) {
|
|
|
564
867
|
}
|
|
565
868
|
async function main() {
|
|
566
869
|
const args = process.argv.slice(2);
|
|
567
|
-
if (args.length === 0
|
|
870
|
+
if (args.length === 0) {
|
|
871
|
+
printHelp();
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
875
|
+
if (args[0] === "run") {
|
|
876
|
+
printRunHelp();
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
568
879
|
printHelp();
|
|
569
880
|
return;
|
|
570
881
|
}
|
|
@@ -674,6 +985,34 @@ async function main() {
|
|
|
674
985
|
});
|
|
675
986
|
return;
|
|
676
987
|
}
|
|
988
|
+
case "run": {
|
|
989
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
990
|
+
printRunHelp();
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const { values } = (0, node_util_1.parseArgs)({
|
|
994
|
+
args: rest,
|
|
995
|
+
options: {
|
|
996
|
+
config: { type: "string" },
|
|
997
|
+
},
|
|
998
|
+
allowPositionals: false,
|
|
999
|
+
});
|
|
1000
|
+
if (!values.config) {
|
|
1001
|
+
printRunHelp();
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
await cmdRun(values.config);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
case "--config": {
|
|
1008
|
+
const configPath = rest[0];
|
|
1009
|
+
if (!configPath || configPath.startsWith("-")) {
|
|
1010
|
+
printRunHelp();
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
await cmdRun(configPath);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
677
1016
|
default:
|
|
678
1017
|
throw new Error(`Unknown command: ${command}. Run ios-simulator-cli --help for usage.`);
|
|
679
1018
|
}
|