@farmslot/adapter-rn 0.15.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/bin/farmslot-adapter-rn.mjs +10 -0
  5. package/dist/agent-device-ui-transport.d.ts +88 -0
  6. package/dist/agent-device-ui-transport.d.ts.map +1 -0
  7. package/dist/agent-device-ui-transport.js +1148 -0
  8. package/dist/agent-device-ui-transport.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +169 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/constants.d.ts +6 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +6 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/doctor.d.ts +17 -0
  18. package/dist/doctor.d.ts.map +1 -0
  19. package/dist/doctor.js +125 -0
  20. package/dist/doctor.js.map +1 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +8 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/json.d.ts +2 -0
  26. package/dist/json.d.ts.map +1 -0
  27. package/dist/json.js +5 -0
  28. package/dist/json.js.map +1 -0
  29. package/dist/metro-recipe-bridge-transport.d.ts +12 -0
  30. package/dist/metro-recipe-bridge-transport.d.ts.map +1 -0
  31. package/dist/metro-recipe-bridge-transport.js +63 -0
  32. package/dist/metro-recipe-bridge-transport.js.map +1 -0
  33. package/dist/redaction.d.ts +3 -0
  34. package/dist/redaction.d.ts.map +1 -0
  35. package/dist/redaction.js +57 -0
  36. package/dist/redaction.js.map +1 -0
  37. package/dist/runner.d.ts +20 -0
  38. package/dist/runner.d.ts.map +1 -0
  39. package/dist/runner.js +270 -0
  40. package/dist/runner.js.map +1 -0
  41. package/dist/scaffold.d.ts +14 -0
  42. package/dist/scaffold.d.ts.map +1 -0
  43. package/dist/scaffold.js +82 -0
  44. package/dist/scaffold.js.map +1 -0
  45. package/dist/simctl-video-recorder.d.ts +6 -0
  46. package/dist/simctl-video-recorder.d.ts.map +1 -0
  47. package/dist/simctl-video-recorder.js +160 -0
  48. package/dist/simctl-video-recorder.js.map +1 -0
  49. package/dist/version.d.ts +3 -0
  50. package/dist/version.d.ts.map +1 -0
  51. package/dist/version.js +11 -0
  52. package/dist/version.js.map +1 -0
  53. package/package.json +81 -0
  54. package/templates/scripts/agentic/recipe/README.md +15 -0
  55. package/templates/scripts/agentic/recipe/action-manifest.json +547 -0
  56. package/templates/scripts/agentic/recipe/action-manifest.with-bridge.json +599 -0
  57. package/templates/scripts/agentic/recipe/recipes/expo.config.recipe.json +37 -0
  58. package/templates/scripts/agentic/validate-recipe.sh +64 -0
  59. package/templates/src/farmslot/RecipeBridgeProvider.tsx +127 -0
  60. package/templates/src/farmslot/RecipeHud.tsx +138 -0
  61. package/templates/src/farmslot/index.ts +11 -0
@@ -0,0 +1,599 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/action-manifest-v1.schema.json",
3
+ "actions": {
4
+ "end": {
5
+ "description": "Finish recipe execution with an explicit status.",
6
+ "examples": [
7
+ {
8
+ "action": "end",
9
+ "status": "pass"
10
+ }
11
+ ]
12
+ },
13
+ "command": {
14
+ "description": "Run a project-local command during setup, diagnostics, or headless proof. Prefer UI actions only when the recipe must prove visible interaction.",
15
+ "schema": {
16
+ "type": "object",
17
+ "properties": {
18
+ "cmd": {
19
+ "type": "string"
20
+ },
21
+ "timeout_ms": {
22
+ "type": "integer"
23
+ },
24
+ "cwd": {
25
+ "type": "string"
26
+ },
27
+ "allow_failure": {
28
+ "type": "boolean"
29
+ }
30
+ },
31
+ "required": ["cmd"],
32
+ "additionalProperties": false
33
+ },
34
+ "examples": [
35
+ {
36
+ "action": "command",
37
+ "intent": "Prepare the project state needed for this proof.",
38
+ "cmd": "true",
39
+ "next": "done"
40
+ }
41
+ ]
42
+ },
43
+ "app.status": {
44
+ "description": "Query the dev-only Farmslot Expo bridge to confirm the app is reachable.",
45
+ "schema": {
46
+ "type": "object",
47
+ "properties": {
48
+ "timeout_ms": {
49
+ "type": "integer"
50
+ }
51
+ },
52
+ "additionalProperties": false
53
+ },
54
+ "examples": [
55
+ {
56
+ "action": "app.status",
57
+ "intent": "Record the resolved application state before continuing.",
58
+ "next": "done"
59
+ }
60
+ ]
61
+ },
62
+ "app.hud": {
63
+ "description": "Display one concise human intent line from the current recipe node.",
64
+ "schema": {
65
+ "type": "object",
66
+ "properties": {
67
+ "clear": {
68
+ "type": "boolean"
69
+ }
70
+ },
71
+ "additionalProperties": false
72
+ },
73
+ "examples": [
74
+ {
75
+ "action": "app.hud",
76
+ "intent": "Show the current goal to the supervising human.",
77
+ "next": "done"
78
+ }
79
+ ]
80
+ },
81
+ "app.trace": {
82
+ "description": "Emit a lightweight bridge trace event for recipe timeline observability.",
83
+ "schema": {
84
+ "type": "object",
85
+ "additionalProperties": false
86
+ },
87
+ "examples": [
88
+ {
89
+ "action": "app.trace",
90
+ "intent": "Record the requested runtime timeline evidence.",
91
+ "next": "done"
92
+ }
93
+ ]
94
+ },
95
+ "ui.press": {
96
+ "description": "Press a native accessibility target on the assigned simulator or device.",
97
+ "schema": {
98
+ "type": "object",
99
+ "properties": {
100
+ "test_id": {
101
+ "type": "string"
102
+ },
103
+ "text": {
104
+ "type": "string"
105
+ },
106
+ "selector": {
107
+ "type": "string"
108
+ }
109
+ },
110
+ "additionalProperties": false
111
+ },
112
+ "examples": [
113
+ {
114
+ "action": "ui.press",
115
+ "intent": "Activate the requested visible control.",
116
+ "test_id": "example-control",
117
+ "next": "done"
118
+ }
119
+ ]
120
+ },
121
+ "ui.key_press": {
122
+ "description": "Send Back through native navigation, or Escape, Enter, or Return to the visible native keyboard, then wait for the resulting UI transition. Keyboard actions fail when no visible keyboard accepts them.",
123
+ "adapters": ["android", "ios"],
124
+ "schema": {
125
+ "type": "object",
126
+ "properties": {
127
+ "key": {
128
+ "type": "string",
129
+ "enum": ["Back", "Escape", "Enter", "Return"]
130
+ },
131
+ "settle": {
132
+ "type": "boolean"
133
+ },
134
+ "timeout_ms": {
135
+ "type": "integer",
136
+ "minimum": 1
137
+ }
138
+ },
139
+ "required": ["key"],
140
+ "additionalProperties": false
141
+ },
142
+ "examples": [
143
+ {
144
+ "action": "ui.key_press",
145
+ "intent": "Continue the requested keyboard interaction.",
146
+ "key": "Enter",
147
+ "next": "done"
148
+ }
149
+ ]
150
+ },
151
+ "ui.set_input": {
152
+ "description": "Set text on a native input. Android recipes must provide test_id so the replacement can be verified from the native snapshot. Values remain part of retained recipe artifacts, so recipes must not contain secrets.",
153
+ "schema": {
154
+ "type": "object",
155
+ "properties": {
156
+ "test_id": {
157
+ "type": "string"
158
+ },
159
+ "value": {
160
+ "type": "string"
161
+ }
162
+ },
163
+ "additionalProperties": false
164
+ },
165
+ "examples": [
166
+ {
167
+ "action": "ui.set_input",
168
+ "intent": "Provide the requested value in the visible form.",
169
+ "test_id": "example-input",
170
+ "value": "example",
171
+ "next": "done"
172
+ }
173
+ ]
174
+ },
175
+ "ui.scroll": {
176
+ "description": "Scroll the assigned native simulator or device and wait for stability.",
177
+ "schema": {
178
+ "type": "object",
179
+ "additionalProperties": false
180
+ },
181
+ "examples": [
182
+ {
183
+ "action": "ui.scroll",
184
+ "intent": "Expose the requested content for visible review.",
185
+ "next": "done"
186
+ }
187
+ ]
188
+ },
189
+ "ui.swipe": {
190
+ "description": "Swipe from a test-id target or explicit screen coordinates in one cardinal direction.",
191
+ "adapters": ["android", "ios"],
192
+ "schema": {
193
+ "type": "object",
194
+ "properties": {
195
+ "target": {
196
+ "type": ["string", "object"],
197
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
198
+ "required": ["x", "y"],
199
+ "additionalProperties": false
200
+ },
201
+ "direction": { "type": "string", "enum": ["up", "down", "left", "right"] },
202
+ "distance": { "type": "number", "minimum": 1 },
203
+ "duration_ms": { "type": "integer", "minimum": 1 },
204
+ "timeout_ms": { "type": "integer", "minimum": 1 },
205
+ "settle": { "type": "boolean" }
206
+ },
207
+ "required": ["target", "direction", "distance", "duration_ms"],
208
+ "additionalProperties": false
209
+ },
210
+ "examples": [
211
+ {
212
+ "action": "ui.swipe",
213
+ "intent": "Swipe the requested visible surface.",
214
+ "target": "example-scrollable",
215
+ "direction": "up",
216
+ "distance": 240,
217
+ "duration_ms": 300,
218
+ "next": "done"
219
+ }
220
+ ]
221
+ },
222
+ "ui.pan": {
223
+ "description": "Stream a continuous pointer path from a test-id target or explicit screen coordinates. Native adapters accept one path point or delta.",
224
+ "adapters": ["android", "ios"],
225
+ "schema": {
226
+ "type": "object",
227
+ "properties": {
228
+ "target": {
229
+ "type": ["string", "object"],
230
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
231
+ "required": ["x", "y"],
232
+ "additionalProperties": false
233
+ },
234
+ "path": {
235
+ "type": "array",
236
+ "minItems": 1,
237
+ "items": {
238
+ "type": "object",
239
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
240
+ "required": ["x", "y"],
241
+ "additionalProperties": false
242
+ }
243
+ },
244
+ "delta": {
245
+ "type": "object",
246
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
247
+ "required": ["x", "y"],
248
+ "additionalProperties": false
249
+ },
250
+ "duration_ms": { "type": "integer", "minimum": 1 },
251
+ "timeout_ms": { "type": "integer", "minimum": 1 },
252
+ "settle": { "type": "boolean" }
253
+ },
254
+ "required": ["target", "duration_ms"],
255
+ "additionalProperties": false
256
+ },
257
+ "adapter_schemas": {
258
+ "android": {
259
+ "type": "object",
260
+ "properties": {
261
+ "path": {
262
+ "type": "array",
263
+ "maxItems": 1,
264
+ "items": { "type": "object", "additionalProperties": true }
265
+ }
266
+ },
267
+ "additionalProperties": true
268
+ },
269
+ "ios": {
270
+ "type": "object",
271
+ "properties": {
272
+ "path": {
273
+ "type": "array",
274
+ "maxItems": 1,
275
+ "items": { "type": "object", "additionalProperties": true }
276
+ }
277
+ },
278
+ "additionalProperties": true
279
+ }
280
+ },
281
+ "examples": [
282
+ {
283
+ "action": "ui.pan",
284
+ "intent": "Pan through the requested pointer path.",
285
+ "target": "example-surface",
286
+ "path": [{ "x": 80, "y": 20 }],
287
+ "duration_ms": 400,
288
+ "next": "done"
289
+ }
290
+ ]
291
+ },
292
+ "ui.drag": {
293
+ "description": "Drag a grabbable test-id target or explicit screen coordinate. Native adapters accept one path point or delta.",
294
+ "adapters": ["android", "ios"],
295
+ "schema": {
296
+ "type": "object",
297
+ "properties": {
298
+ "target": {
299
+ "type": ["string", "object"],
300
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
301
+ "required": ["x", "y"],
302
+ "additionalProperties": false
303
+ },
304
+ "path": {
305
+ "type": "array",
306
+ "minItems": 1,
307
+ "items": {
308
+ "type": "object",
309
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
310
+ "required": ["x", "y"],
311
+ "additionalProperties": false
312
+ }
313
+ },
314
+ "delta": {
315
+ "type": "object",
316
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
317
+ "required": ["x", "y"],
318
+ "additionalProperties": false
319
+ },
320
+ "duration_ms": { "type": "integer", "minimum": 1 },
321
+ "timeout_ms": { "type": "integer", "minimum": 1 },
322
+ "settle": { "type": "boolean" }
323
+ },
324
+ "required": ["target", "duration_ms"],
325
+ "additionalProperties": false
326
+ },
327
+ "adapter_schemas": {
328
+ "android": {
329
+ "type": "object",
330
+ "properties": {
331
+ "path": {
332
+ "type": "array",
333
+ "maxItems": 1,
334
+ "items": { "type": "object", "additionalProperties": true }
335
+ }
336
+ },
337
+ "additionalProperties": true
338
+ },
339
+ "ios": {
340
+ "type": "object",
341
+ "properties": {
342
+ "path": {
343
+ "type": "array",
344
+ "maxItems": 1,
345
+ "items": { "type": "object", "additionalProperties": true }
346
+ }
347
+ },
348
+ "additionalProperties": true
349
+ }
350
+ },
351
+ "examples": [
352
+ {
353
+ "action": "ui.drag",
354
+ "intent": "Drag the requested control to a new value.",
355
+ "target": "example-slider",
356
+ "delta": { "x": 120, "y": 0 },
357
+ "duration_ms": 500,
358
+ "next": "done"
359
+ }
360
+ ]
361
+ },
362
+ "ui.long_press": {
363
+ "description": "Hold a test-id target or explicit screen coordinate for a bounded duration.",
364
+ "adapters": ["android", "ios"],
365
+ "schema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "target": {
369
+ "type": ["string", "object"],
370
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } },
371
+ "required": ["x", "y"],
372
+ "additionalProperties": false
373
+ },
374
+ "hold_ms": { "type": "integer", "minimum": 1 },
375
+ "timeout_ms": { "type": "integer", "minimum": 1 },
376
+ "settle": { "type": "boolean" }
377
+ },
378
+ "required": ["target", "hold_ms"],
379
+ "additionalProperties": false
380
+ },
381
+ "examples": [
382
+ {
383
+ "action": "ui.long_press",
384
+ "intent": "Hold the requested visible control.",
385
+ "target": "example-control",
386
+ "hold_ms": 700,
387
+ "next": "done"
388
+ }
389
+ ]
390
+ },
391
+ "ui.wait_for": {
392
+ "description": "Wait for native accessibility content and UI stability.",
393
+ "schema": {
394
+ "type": "object",
395
+ "properties": {
396
+ "text_contains": {
397
+ "type": "array",
398
+ "items": {
399
+ "type": "string"
400
+ }
401
+ },
402
+ "timeout_ms": {
403
+ "type": "integer"
404
+ },
405
+ "text": {
406
+ "type": "string"
407
+ },
408
+ "target": {
409
+ "type": "string"
410
+ },
411
+ "test_id": {
412
+ "type": "string"
413
+ },
414
+ "visible": {
415
+ "type": "boolean"
416
+ },
417
+ "event": {
418
+ "type": "string"
419
+ },
420
+ "selector": {
421
+ "type": "string"
422
+ },
423
+ "expected": {
424
+ "type": "string"
425
+ }
426
+ },
427
+ "additionalProperties": false
428
+ },
429
+ "examples": [
430
+ {
431
+ "action": "ui.wait_for",
432
+ "intent": "Confirm the requested application state is visible.",
433
+ "test_id": "example-control",
434
+ "visible": true,
435
+ "next": "done"
436
+ }
437
+ ]
438
+ },
439
+ "ui.screenshot": {
440
+ "description": "Capture the assigned native simulator or device into the artifact package.",
441
+ "schema": {
442
+ "type": "object",
443
+ "properties": {
444
+ "path": {
445
+ "type": "string"
446
+ },
447
+ "label": {
448
+ "type": "string"
449
+ },
450
+ "claims": {
451
+ "type": "object",
452
+ "properties": {
453
+ "must_show": {
454
+ "type": "array",
455
+ "items": {
456
+ "type": "string"
457
+ }
458
+ },
459
+ "must_not_show": {
460
+ "type": "array",
461
+ "items": {
462
+ "type": "string"
463
+ }
464
+ }
465
+ },
466
+ "additionalProperties": false
467
+ },
468
+ "covers": {
469
+ "type": "array",
470
+ "items": {
471
+ "type": "string"
472
+ }
473
+ },
474
+ "note": {
475
+ "type": "string"
476
+ }
477
+ },
478
+ "additionalProperties": false
479
+ },
480
+ "examples": [
481
+ {
482
+ "action": "ui.screenshot",
483
+ "intent": "Preserve the visible application state as review evidence.",
484
+ "label": "example-state",
485
+ "next": "done"
486
+ }
487
+ ]
488
+ },
489
+ "ui.capture_surface": {
490
+ "description": "Capture the full current scrollable screen as one PNG. This action is opt-in; ui.screenshot remains viewport-only.",
491
+ "schema": {
492
+ "type": "object",
493
+ "properties": {
494
+ "path": { "type": "string" },
495
+ "label": { "type": "string" },
496
+ "surface_test_id": { "type": "string" },
497
+ "until_test_id": { "type": "string" },
498
+ "max_scrolls": { "type": "integer" },
499
+ "restore_position": { "type": "boolean" }
500
+ },
501
+ "additionalProperties": false
502
+ },
503
+ "examples": [
504
+ {
505
+ "action": "ui.capture_surface",
506
+ "intent": "Preserve the complete scrollable context for human review.",
507
+ "path": "screenshots/full-screen.png",
508
+ "surface_test_id": "screen-scroll-surface",
509
+ "until_test_id": "screen-bottom",
510
+ "next": "done"
511
+ }
512
+ ]
513
+ },
514
+ "assert_output": {
515
+ "description": "assert_output action.",
516
+ "schema": {
517
+ "type": "object",
518
+ "properties": {
519
+ "source": {
520
+ "type": "string"
521
+ },
522
+ "stream": {
523
+ "type": "string"
524
+ },
525
+ "contains": {
526
+ "type": "string"
527
+ },
528
+ "match": {
529
+ "type": "string"
530
+ },
531
+ "assert": {
532
+ "type": "object",
533
+ "additionalProperties": true
534
+ }
535
+ },
536
+ "required": ["source"],
537
+ "additionalProperties": false
538
+ },
539
+ "examples": [
540
+ {
541
+ "action": "assert_output",
542
+ "intent": "Confirm the recorded output satisfies the requested condition.",
543
+ "source": "command",
544
+ "contains": "ready",
545
+ "next": "done"
546
+ }
547
+ ]
548
+ },
549
+ "wait": {
550
+ "description": "Pause briefly for asynchronous setup work. Keep waits short and prefer deterministic assertions.",
551
+ "schema": {
552
+ "type": "object",
553
+ "properties": {
554
+ "duration_ms": {
555
+ "type": "integer"
556
+ }
557
+ },
558
+ "required": ["duration_ms"],
559
+ "additionalProperties": false
560
+ },
561
+ "examples": [
562
+ {
563
+ "action": "wait",
564
+ "intent": "Allow the requested asynchronous state to settle.",
565
+ "duration_ms": 1,
566
+ "next": "done"
567
+ }
568
+ ]
569
+ }
570
+ },
571
+ "observers": [
572
+ {
573
+ "ref": "ui.screen",
574
+ "default_for": [
575
+ "ui.press",
576
+ "ui.set_input",
577
+ "ui.scroll",
578
+ "ui.swipe",
579
+ "ui.pan",
580
+ "ui.drag",
581
+ "ui.long_press",
582
+ "ui.wait_for"
583
+ ]
584
+ },
585
+ {
586
+ "ref": "ui.visible",
587
+ "default_for": [
588
+ "ui.press",
589
+ "ui.set_input",
590
+ "ui.scroll",
591
+ "ui.swipe",
592
+ "ui.pan",
593
+ "ui.drag",
594
+ "ui.long_press",
595
+ "ui.wait_for"
596
+ ]
597
+ }
598
+ ]
599
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
+ "title": "Expo configuration smoke",
4
+ "description": "Verifies the Expo project can run a Farmslot Recipe Protocol v1 artifact-producing configuration check.",
5
+ "proofTargets": [
6
+ {
7
+ "id": "expo-config",
8
+ "claim": "The Expo project can execute a Farmslot recipe runner and expose public config without secrets."
9
+ }
10
+ ],
11
+ "workflow": {
12
+ "entry": "expo-config",
13
+ "nodes": {
14
+ "expo-config": {
15
+ "action": "command",
16
+ "intent": "Read Expo public config through the project package manager",
17
+ "cmd": "yarn expo config --type public --json",
18
+ "timeout_ms": 120000,
19
+ "next": "assert-config",
20
+ "proves": ["expo-config"]
21
+ },
22
+ "assert-config": {
23
+ "action": "assert_output",
24
+ "intent": "Confirm the Expo public config contains a slug field",
25
+ "source": "expo-config",
26
+ "stream": "stdout",
27
+ "contains": "\"slug\"",
28
+ "next": "done",
29
+ "proves": ["expo-config"]
30
+ },
31
+ "done": {
32
+ "action": "end",
33
+ "status": "pass"
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ APP_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
6
+
7
+ RECIPE_PATH="scripts/agentic/recipe/recipes/expo.config.recipe.json"
8
+ ARTIFACTS_DIR=""
9
+ DRY_RUN=0
10
+
11
+ value_from_equals() {
12
+ local option="$1"
13
+ local value="${option#*=}"
14
+ if [[ -z "${value}" ]]; then
15
+ echo "ERROR: ${option%%=*} requires a value." >&2
16
+ exit 1
17
+ fi
18
+ printf '%s' "${value}"
19
+ }
20
+
21
+ require_value() {
22
+ local option="$1"
23
+ local value="${2:-}"
24
+ if [[ -z "${value}" || "${value}" == --* ]]; then
25
+ echo "ERROR: ${option} requires a value." >&2
26
+ exit 1
27
+ fi
28
+ printf '%s' "${value}"
29
+ }
30
+
31
+ while [[ "$#" -gt 0 ]]; do
32
+ case "$1" in
33
+ --recipe)
34
+ RECIPE_PATH="$(require_value "$1" "${2:-}")"; shift 2 ;;
35
+ --recipe=*)
36
+ RECIPE_PATH="$(value_from_equals "$1")"; shift ;;
37
+ --artifacts-dir)
38
+ ARTIFACTS_DIR="$(require_value "$1" "${2:-}")"; shift 2 ;;
39
+ --artifacts-dir=*)
40
+ ARTIFACTS_DIR="$(value_from_equals "$1")"; shift ;;
41
+ --dry-run)
42
+ DRY_RUN=1; shift ;;
43
+ --runtime-dir|--platform|--metro-port|--simulator|--adb-serial)
44
+ require_value "$1" "${2:-}" >/dev/null; shift 2 ;;
45
+ --runtime-dir=*|--platform=*|--metro-port=*|--simulator=*|--adb-serial=*)
46
+ shift ;;
47
+ *)
48
+ echo "ERROR: unknown recipe validation option '$1'." >&2
49
+ exit 1 ;;
50
+ esac
51
+ done
52
+
53
+ if [[ -z "${ARTIFACTS_DIR}" ]]; then
54
+ echo "ERROR: --artifacts-dir is required." >&2
55
+ exit 1
56
+ fi
57
+
58
+ ARGS=(farmslot-adapter-rn run "${RECIPE_PATH}" --artifacts-dir "${ARTIFACTS_DIR}")
59
+ if [[ "${DRY_RUN}" -eq 1 ]]; then
60
+ ARGS+=(--dry-run)
61
+ fi
62
+
63
+ cd "${APP_DIR}"
64
+ yarn "${ARGS[@]}"