@deeeed/metamask-harness 0.2.0 → 0.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +47 -17
  3. package/{orchestration → adapters}/core/cleanup.sh +2 -2
  4. package/{orchestration → adapters}/core/inject.sh +8 -8
  5. package/{orchestration → adapters}/extension/cleanup.mjs +1 -1
  6. package/{orchestration → adapters}/extension/console-tail.mjs +3 -0
  7. package/{orchestration → adapters}/extension/ensure-browser.sh +4 -4
  8. package/{orchestration → adapters}/extension/inject.mjs +27 -31
  9. package/{orchestration → adapters}/extension/launch.sh +4 -4
  10. package/{orchestration → adapters}/extension/live.sh +5 -5
  11. package/{orchestration → adapters}/extension/readiness.mjs +12 -2
  12. package/{orchestration → adapters}/extension/refresh-build.sh +1 -1
  13. package/{orchestration → adapters}/extension/seed-fixture.sh +1 -1
  14. package/{orchestration → adapters}/extension/start-watch.sh +7 -7
  15. package/{runner → adapters}/extension/verify.sh +11 -11
  16. package/{orchestration → adapters}/extension/wallet-fixture-state.cjs +2 -2
  17. package/{orchestration → adapters}/manifest.json +96 -96
  18. package/{library/actions → adapters}/mobile/bridge-runtime/cdp-bridge.cjs +5 -5
  19. package/{orchestration → adapters}/mobile/cleanup.sh +3 -3
  20. package/{orchestration → adapters}/mobile/inject.sh +22 -24
  21. package/adapters/mobile/open-device.sh +260 -0
  22. package/{orchestration/compat-overlays → adapters/mobile/overlay/compat}/README.md +13 -4
  23. package/adapters/mobile/prewarm-bundle.sh +95 -0
  24. package/adapters/mobile/start-metro.sh +228 -0
  25. package/{runner → adapters}/mobile/verify.sh +6 -6
  26. package/adapters/mobile/wait-for-bridge.sh +108 -0
  27. package/adapters/mobile/yarn-setup.sh +20 -0
  28. package/{orchestration/lib → adapters/shared}/cli-ux.sh +5 -9
  29. package/{orchestration/lib → adapters/shared}/log-tui.mjs +57 -1
  30. package/{orchestration/lib → adapters/shared}/open-debug.mjs +32 -10
  31. package/{orchestration/lib → adapters/shared}/resolve-farmslot-ports.sh +47 -0
  32. package/adapters/shared/sync-wallet-fixture.sh +78 -0
  33. package/bin/mm-harness +37 -6
  34. package/docs/CLI-SPEC.md +41 -2
  35. package/docs/architecture.md +69 -38
  36. package/docs/runtime-file-conventions.md +3 -3
  37. package/library/actions/core/perps/_controller.mjs +1 -1
  38. package/library/actions/extension/platform/cdp.mjs +2 -2
  39. package/library/actions/extension/wallet/ensure_unlocked.mjs +1 -1
  40. package/library/actions/mobile/platform/bridge.mjs +1 -1
  41. package/library/actions/mobile/wallet/ensure_unlocked.mjs +1 -1
  42. package/library/actions/mobile/wallet/setup.mjs +2 -2
  43. package/library/library.json +1 -1
  44. package/package.json +8 -8
  45. package/{orchestration → src/adapters}/extension/runtime-decision.ts +5 -5
  46. package/{orchestration → src/adapters}/extension/runtime.ts +1 -1
  47. package/src/adapters/mobile/prepare.ts +210 -0
  48. package/{orchestration → src/adapters}/mobile/runtime-decision.ts +105 -5
  49. package/{runner/src → src}/adapters.ts +3 -3
  50. package/{orchestration/lib/cli-color.mjs → src/cli-color.ts} +21 -13
  51. package/{orchestration/lib/cli-commands.mjs → src/cli-commands.ts} +52 -45
  52. package/{orchestration/lib/cli-version.mjs → src/cli-version.ts} +35 -17
  53. package/{runner/src → src}/cli.ts +48 -40
  54. package/src/commands/debug.ts +63 -0
  55. package/src/commands/fixtures.ts +193 -0
  56. package/{runner/src → src}/commands/launch.ts +200 -54
  57. package/src/commands/logs.ts +83 -0
  58. package/{runner/src → src}/commands/shared.ts +40 -30
  59. package/src/commands/update.ts +316 -0
  60. package/{runner/src → src}/harness.ts +119 -16
  61. package/{runner/src → src}/heal-bounds.ts +20 -1
  62. package/src/index.ts +15 -0
  63. package/{runner/src → src}/live-adapter-contract.ts +1 -1
  64. package/{runner/src → src}/mm-harness-cli.ts +64 -3
  65. package/{runner/src → src}/paths.ts +2 -2
  66. package/{orchestration/lib/progress.mjs → src/progress.ts} +31 -21
  67. package/docs/extension-runtime-commands.md +0 -60
  68. package/docs/harness-cli.md +0 -43
  69. package/orchestration/compat-overlays/mobile/README.md +0 -13
  70. package/orchestration/doctor.mjs +0 -72
  71. package/orchestration/lib/cli-home.mjs +0 -354
  72. package/orchestration/lib/recipe-paths.mjs +0 -26
  73. package/orchestration/mobile/launch.sh +0 -137
  74. package/orchestration/mobile/live.sh +0 -125
  75. package/orchestration/porcelain/metamask-recipe +0 -99
  76. package/orchestration/porcelain/mm-recipe +0 -1591
  77. package/orchestration/porcelain/mme-recipe +0 -1181
  78. package/runner/src/commands/debug.ts +0 -44
  79. package/runner/src/commands/fixtures.ts +0 -99
  80. package/runner/src/commands/logs.ts +0 -60
  81. package/runner/src/index.ts +0 -6
  82. /package/{orchestration → adapters}/extension/launch-browser.cjs +0 -0
  83. /package/{orchestration → adapters}/extension/pin-remote-flags.cjs +0 -0
  84. /package/{orchestration → adapters}/extension/sidepanel-toggle.sh +0 -0
  85. /package/{orchestration → adapters}/extension/snapshot-dist.sh +0 -0
  86. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -0
  87. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/config.cjs +0 -0
  88. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/issue-capture.cjs +0 -0
  89. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/target-discovery.cjs +0 -0
  90. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/ws-client.cjs +0 -0
  91. /package/{library/actions → adapters}/mobile/bridge-runtime/setup-wallet.sh +0 -0
  92. /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -0
  93. /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -0
  94. /package/{orchestration/compat-overlays/mobile → adapters/mobile/overlay/compat}/rn81-message-event-source.patch +0 -0
  95. /package/{orchestration/lib → adapters/shared}/activate-repo-node.sh +0 -0
  96. /package/{orchestration/lib → adapters/shared}/ensure-runner-deps.sh +0 -0
  97. /package/{orchestration/lib → adapters/shared}/harness-path.sh +0 -0
  98. /package/{orchestration/lib → adapters/shared}/hash-helpers.sh +0 -0
  99. /package/{orchestration/lib → adapters/shared}/json-field.sh +0 -0
  100. /package/{orchestration/lib → adapters/shared}/path-defaults.json +0 -0
  101. /package/{orchestration → src/adapters}/extension/ensure-ready.ts +0 -0
  102. /package/{orchestration → src/adapters}/extension/extension-id.ts +0 -0
  103. /package/{orchestration → src/adapters}/mobile/deps-markers.ts +0 -0
  104. /package/{runner/src → src}/completions-cache.ts +0 -0
  105. /package/{runner/src → src}/doctor.ts +0 -0
  106. /package/{runner/src → src}/manifest.ts +0 -0
  107. /package/{runner/src → src}/recording-target.ts +0 -0
  108. /package/{runner/src → src}/run-recording.ts +0 -0
  109. /package/{runner/src → src}/runner.ts +0 -0
  110. /package/{runner/src → src}/types.ts +0 -0
@@ -42,21 +42,21 @@ done
42
42
 
43
43
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
44
44
  RUNNER_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd -P)"
45
- ADAPTER_DIR="$RUNNER_DIR/library/actions/mobile"
46
- # compat-overlays: NOT applied here. orchestration/compat-overlays/ holds
45
+ ADAPTER_DIR="$RUNNER_DIR/adapters/mobile"
46
+ # compat-overlays: NOT applied here. adapters/mobile/overlay/compat/ holds
47
47
  # reversible patches applied only when injecting into HISTORICAL checkouts
48
48
  # (replay/eval against old product commits, e.g. pre-RN-0.81 mobile); the
49
49
  # operator/recipe-harness skill applies them manually before the rebuild.
50
- # See orchestration/compat-overlays/README.md.
50
+ # See adapters/mobile/overlay/compat/README.md.
51
51
  # shellcheck disable=SC1091
52
- . "$SCRIPT_DIR/../lib/hash-helpers.sh"
52
+ . "$SCRIPT_DIR/../shared/hash-helpers.sh"
53
53
  # shellcheck disable=SC1091
54
- for _hp in "$SCRIPT_DIR/../lib/harness-path.sh"; do
54
+ for _hp in "$SCRIPT_DIR/../shared/harness-path.sh"; do
55
55
  [ -f "$_hp" ] && { . "$_hp"; break; }
56
56
  done
57
57
  unset _hp
58
58
  if ! command -v harness_root >/dev/null 2>&1; then
59
- echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
59
+ echo "mm-harness: shared lib adapters/shared/harness-path.sh not found; reinstall the runner." >&2
60
60
  exit 1
61
61
  fi
62
62
  TARGET="$(cd "$TARGET" && pwd)"
@@ -134,7 +134,7 @@ install_v1_runner_assets() {
134
134
  # quote — cannot break the generated wrapper or inject at runtime.
135
135
  local runner_protocol_root_q runner_exec_q target_q
136
136
  runner_protocol_root_q="$(printf '%q' "$METAMASK_RUNNER_PROTOCOL_ROOT")"
137
- runner_exec_q="$(printf '%q' "$METAMASK_RUNNER_DIR/orchestration/porcelain/metamask-recipe")"
137
+ runner_exec_q="$(printf '%q' "$METAMASK_RUNNER_DIR/bin/mm-harness")"
138
138
  target_q="$(printf '%q' "$TARGET")"
139
139
  {
140
140
  printf '%s\n' '#!/usr/bin/env bash'
@@ -175,8 +175,8 @@ install_v1_runner_assets() {
175
175
  printf 'export FARMSLOT_ROOT=${FARMSLOT_ROOT:-%s}\n' "$runner_protocol_root_q"
176
176
  fi
177
177
  printf 'exec %s "$@"\n' "$runner_exec_q"
178
- } > "$HARNESS_DIR/runner/bin/metamask-recipe"
179
- chmod +x "$HARNESS_DIR/runner/bin/metamask-recipe"
178
+ } > "$HARNESS_DIR/runner/bin/mm-harness"
179
+ chmod +x "$HARNESS_DIR/runner/bin/mm-harness"
180
180
  if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
181
181
  printf '%s\n' "$METAMASK_RUNNER_PROTOCOL_ROOT" > "$HARNESS_DIR/runner/.farmslot-root"
182
182
  fi
@@ -199,19 +199,19 @@ install_v1_runner_assets() {
199
199
  # Installed-layout back-compat (INTENTIONAL): targets keep a flat
200
200
  # <harness>/scripts/ subtree assembled from the runner trees, so installed
201
201
  # consumers are unaffected by runner-repo layout changes.
202
- if [ -d "$METAMASK_RUNNER_DIR/orchestration/mobile" ]; then
202
+ if [ -d "$METAMASK_RUNNER_DIR/adapters/mobile" ]; then
203
203
  mkdir -p "$HARNESS_DIR/scripts"
204
- cp "$METAMASK_RUNNER_DIR/orchestration/mobile/launch.sh" "$HARNESS_DIR/scripts/launch.sh"
205
- cp "$METAMASK_RUNNER_DIR/orchestration/mobile/live.sh" "$HARNESS_DIR/scripts/live.sh"
206
- cp "$METAMASK_RUNNER_DIR/runner/mobile/verify.sh" "$HARNESS_DIR/scripts/verify.sh"
204
+ # launch and live orchestration is now in-process (src/harness.ts + src/adapters/mobile/prepare.ts).
205
+ # Remove any copies left by older installs so stale scripts cannot shadow the in-process path.
206
+ rm -f "$HARNESS_DIR/scripts/launch.sh" "$HARNESS_DIR/scripts/live.sh"
207
+ cp "$METAMASK_RUNNER_DIR/adapters/mobile/verify.sh" "$HARNESS_DIR/scripts/verify.sh"
207
208
  mkdir -p "$HARNESS_DIR/scripts/lib"
208
- cp "$METAMASK_RUNNER_DIR/orchestration/lib/harness-path.sh" "$HARNESS_DIR/scripts/lib/harness-path.sh"
209
- cp "$METAMASK_RUNNER_DIR/orchestration/lib/path-defaults.json" "$HARNESS_DIR/scripts/lib/path-defaults.json"
210
- cp "$METAMASK_RUNNER_DIR/orchestration/lib/recipe-paths.mjs" "$HARNESS_DIR/scripts/lib/recipe-paths.mjs"
209
+ cp "$METAMASK_RUNNER_DIR/adapters/shared/harness-path.sh" "$HARNESS_DIR/scripts/lib/harness-path.sh"
210
+ cp "$METAMASK_RUNNER_DIR/adapters/shared/path-defaults.json" "$HARNESS_DIR/scripts/lib/path-defaults.json"
211
211
  chmod -R u+rwX,go+rX "$HARNESS_DIR/scripts"
212
212
  find "$HARNESS_DIR/scripts" -type f \( -name '*.sh' -o -name '*.cjs' -o -name '*.mjs' \) -exec chmod +x {} +
213
213
  fi
214
- if [ ! -x "$HARNESS_DIR/runner/bin/metamask-recipe" ]; then
214
+ if [ ! -x "$HARNESS_DIR/runner/bin/mm-harness" ]; then
215
215
  echo "Refusing mobile recipe harness install: failed to make runner executable." >&2
216
216
  return 1
217
217
  fi
@@ -288,10 +288,8 @@ if [ "$FORCE_OVERLAY" = false ] && has_product_owned_mobile_harness; then
288
288
  target,
289
289
  protocolVersion: "v1",
290
290
  actionManifestPath: harnessRel + "/action-manifest.json",
291
- runnerEntrypoint: harnessRel + "/runner/bin/metamask-recipe",
291
+ runnerEntrypoint: harnessRel + "/runner/bin/mm-harness",
292
292
  runtimeHelpers: {
293
- launch: harnessRel + "/scripts/launch.sh",
294
- live: harnessRel + "/scripts/live.sh",
295
293
  verify: harnessRel + "/scripts/verify.sh"
296
294
  },
297
295
  installedPaths: [],
@@ -527,7 +525,7 @@ install_v1_runner_assets
527
525
 
528
526
  mkdir -p "$TARGET/app/dev-tools/AgenticService"
529
527
  while IFS= read -r -d '' overlay_file; do
530
- rel="${overlay_file#$ADAPTER_DIR/app-overlay/app/dev-tools/AgenticService/}"
528
+ rel="${overlay_file#$ADAPTER_DIR/overlay/app/dev-tools/AgenticService/}"
531
529
  dest_rel="${rel%.patch}"
532
530
  dest="$TARGET/app/dev-tools/AgenticService/$dest_rel"
533
531
  mkdir -p "$(dirname "$dest")"
@@ -537,7 +535,7 @@ while IFS= read -r -d '' overlay_file; do
537
535
  # Exclude overlay *.test.* files: they are source tests for the overlay
538
536
  # templates and must not ship into the product checkout, where the product's
539
537
  # jest could pick them up on local runs.
540
- done < <(find "$ADAPTER_DIR/app-overlay/app/dev-tools/AgenticService" -type f ! -name '*.test.*' -print0)
538
+ done < <(find "$ADAPTER_DIR/overlay/app/dev-tools/AgenticService" -type f ! -name '*.test.*' -print0)
541
539
 
542
540
  node - "$TARGET" <<'NODE'
543
541
  const fs = require('fs');
@@ -654,7 +652,7 @@ node -e '
654
652
  target,
655
653
  protocolVersion: "v1",
656
654
  actionManifestPath: harnessRel + "/action-manifest.json",
657
- runnerEntrypoint: harnessRel + "/runner/bin/metamask-recipe",
655
+ runnerEntrypoint: harnessRel + "/runner/bin/mm-harness",
658
656
  runtimeHelpers: {
659
657
  launch: harnessRel + "/scripts/launch.sh",
660
658
  live: harnessRel + "/scripts/live.sh",
@@ -673,7 +671,7 @@ node -e '
673
671
  fs.writeFileSync(manifestPath, JSON.stringify(m, null, 2) + "\n");
674
672
  ' "$RUNNER_DIR" "$SOURCE_REV" "$METAMASK_RUNNER_DIR" "$METAMASK_RUNNER_REVISION" "$METAMASK_RUNNER_SOURCE_KIND" "$ADAPTER_DIR" "$TARGET" "$BACKUP_DIR" "$SCRIPT_DIR" "$HARNESS_DIR/manifest.json" "${MOBILE_AGENTIC_SOURCE:-}" "$HARNESS_ROOT" "$HARNESS_REL" "$CLEANUP_COMMAND"
675
673
 
676
- ENSURE_DEPS="$SCRIPT_DIR/../lib/ensure-runner-deps.sh"
674
+ ENSURE_DEPS="$SCRIPT_DIR/../shared/ensure-runner-deps.sh"
677
675
  if [ -f "$ENSURE_DEPS" ]; then
678
676
  bash "$ENSURE_DEPS" "$RUNNER_DIR"
679
677
  fi
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env bash
2
+ # open-device — open the MetaMask Mobile dev client on a simulator or device.
3
+ #
4
+ # iOS: boots simulator if needed, writes EXDevMenuIsOnboardingFinished default,
5
+ # then opens via the expo-development-client deep link (disableOnboarding=1).
6
+ # Android: sets adb reverse for Metro port reachability, then opens via deep link.
7
+ # Preflight-mode guard: fast (teaching error if not installed), auto (install if missing),
8
+ # rebuild-native / clean (always rebuild), default (same as auto).
9
+ #
10
+ # Inputs:
11
+ # --platform ios|android (default ios)
12
+ # --target <metamask-mobile dir> (default $PWD)
13
+ # --simulator <udid|name|"booted"> iOS simulator (default: IOS_SIMULATOR env, else "booted")
14
+ # --adb-serial <serial> Android device serial (default: ADB_SERIAL env)
15
+ # --preflight-mode fast|auto|rebuild-native|clean (default: fast; env MOBILE_PREFLIGHT_MODE)
16
+ # Outputs:
17
+ # Progress on stderr; exit 0 pass; 1 open failed; 2 bad args.
18
+ #
19
+ # Single device op: open the dev client. Does not start Metro or install node_modules.
20
+ set -uo pipefail
21
+
22
+ PLATFORM="ios"
23
+ TARGET="$PWD"
24
+ SIMULATOR="${IOS_SIMULATOR:-${SIM_UDID:-booted}}"
25
+ ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
26
+ PREFLIGHT_MODE="${MOBILE_PREFLIGHT_MODE:-fast}"
27
+ PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
28
+
29
+ while [ "$#" -gt 0 ]; do
30
+ case "$1" in
31
+ --platform) PLATFORM="$2"; shift 2 ;;
32
+ --target) TARGET="$2"; shift 2 ;;
33
+ --simulator) SIMULATOR="$2"; shift 2 ;;
34
+ --adb-serial) ADB_SERIAL_ARG="$2"; shift 2 ;;
35
+ --preflight-mode) PREFLIGHT_MODE="$2"; shift 2 ;;
36
+ --port) PORT="$2"; shift 2 ;;
37
+ -h|--help)
38
+ printf 'Usage: open-device.sh [--platform ios|android] [--target <dir>]\n'
39
+ printf ' [--simulator <udid|name|booted>] [--adb-serial <serial>]\n'
40
+ printf ' [--preflight-mode fast|auto|rebuild-native|clean] [--port <n>]\n'
41
+ exit 0
42
+ ;;
43
+ *) printf 'open-device: unknown arg: %s\n' "$1" >&2; exit 2 ;;
44
+ esac
45
+ done
46
+
47
+ case "$PLATFORM" in
48
+ ios|android) ;;
49
+ *) printf 'open-device: --platform must be ios or android (got: %s)\n' "$PLATFORM" >&2; exit 2 ;;
50
+ esac
51
+
52
+ TARGET="$(cd "$TARGET" && pwd)"
53
+
54
+ # --- iOS helpers --------------------------------------------------------------
55
+
56
+ ios_app_installed() {
57
+ local sim_target="$1" bundle_id="$2"
58
+ xcrun simctl get_app_container "$sim_target" "$bundle_id" app >/dev/null 2>&1
59
+ }
60
+
61
+ run_ios_native_build() {
62
+ local sim_target="$1" reason="$2"
63
+ printf '%s; installing with yarn start:ios (WATCHER_PORT=%s)\n' "$reason" "$PORT" >&2
64
+ (cd "$TARGET" && WATCHER_PORT="$PORT" METRO_PORT="$PORT" IOS_SIMULATOR="$sim_target" EXPO_NO_TYPESCRIPT_SETUP=1 yarn start:ios 2>&1)
65
+ }
66
+
67
+ ensure_ios_app_for_mode() {
68
+ local sim_target="$1" bundle_id="$2" mode="$3"
69
+ case "$mode" in
70
+ fast)
71
+ if ios_app_installed "$sim_target" "$bundle_id"; then return 0; fi
72
+ printf >&2 'open-device: fast mode requires an installed iOS dev client (%s) on %s.\n' \
73
+ "$bundle_id" "$sim_target"
74
+ printf >&2 'Fast mode only prewarms the bundle and opens the existing client.\n'
75
+ printf >&2 'Next: use --preflight-mode auto to install if missing.\n'
76
+ return 1
77
+ ;;
78
+ auto|default)
79
+ if ios_app_installed "$sim_target" "$bundle_id"; then return 0; fi
80
+ run_ios_native_build "$sim_target" "iOS dev client ${bundle_id} not installed on ${sim_target}"
81
+ ;;
82
+ rebuild-native|clean)
83
+ run_ios_native_build "$sim_target" "Rebuilding iOS dev client ${bundle_id} (--preflight-mode ${mode})"
84
+ ;;
85
+ *)
86
+ printf 'open-device: unknown --preflight-mode: %s\n' "$mode" >&2; return 2 ;;
87
+ esac
88
+ }
89
+
90
+ sim_udid_for_target() {
91
+ local target="$1"
92
+ # Already a UDID
93
+ if printf '%s' "$target" | grep -Eq '^[0-9A-Fa-f-]{36}$'; then
94
+ printf '%s\n' "$target"; return 0
95
+ fi
96
+ xcrun simctl list devices available 2>/dev/null \
97
+ | sed -n "s/.*${target} (\([0-9A-Fa-f-]*\)) (.*/\1/p" | head -1
98
+ }
99
+
100
+ boot_simulator_if_needed() {
101
+ local target="$1" state=""
102
+ [ "$target" != "booted" ] || return 0
103
+ local line=""
104
+ line="$(xcrun simctl list devices available 2>/dev/null | grep -F "${target} (" | head -1 || true)"
105
+ case "$line" in *"(Booted)"*) state="Booted" ;; *"(Shutdown)"*) state="Shutdown" ;; esac
106
+ [ "$state" = "Booted" ] && return 0
107
+ printf 'Booting iOS simulator %s%s\n' "$target" "${state:+ ($state)}" >&2
108
+ xcrun simctl boot "$target" 2>/dev/null || true
109
+ xcrun simctl bootstatus "$target" -b >/dev/null
110
+ }
111
+
112
+ show_simulator() {
113
+ local target="$1" udid=""
114
+ if [ "$target" = "booted" ]; then
115
+ printf 'Opening Simulator UI\n' >&2
116
+ open -a Simulator >/dev/null 2>&1 || true
117
+ else
118
+ udid="$(sim_udid_for_target "$target")"
119
+ if [ -n "$udid" ]; then
120
+ open -a Simulator --args -CurrentDeviceUDID "$udid" >/dev/null 2>&1 || true
121
+ else
122
+ open -a Simulator >/dev/null 2>&1 || true
123
+ fi
124
+ fi
125
+ osascript -e 'tell application "Simulator" to activate' >/dev/null 2>&1 || true
126
+ }
127
+
128
+ # --- Android helpers ----------------------------------------------------------
129
+
130
+ android_app_installed() {
131
+ local pkg="$1"
132
+ shift
133
+ adb "$@" shell pm list packages 2>/dev/null | grep -q "package:${pkg}"
134
+ }
135
+
136
+ run_android_native_build() {
137
+ local reason="$1"
138
+ printf '%s; installing with yarn start:android (WATCHER_PORT=%s)\n' "$reason" "$PORT" >&2
139
+ (cd "$TARGET" && WATCHER_PORT="$PORT" yarn start:android 2>&1)
140
+ }
141
+
142
+ ensure_android_app_for_mode() {
143
+ local pkg="$1" mode="$2"
144
+ shift 2
145
+ case "$mode" in
146
+ fast)
147
+ if android_app_installed "$pkg" "$@"; then return 0; fi
148
+ printf >&2 'open-device: fast mode requires an installed Android dev client (%s).\n' "$pkg"
149
+ printf >&2 'Next: use --preflight-mode auto to install if missing.\n'
150
+ return 1
151
+ ;;
152
+ auto|default)
153
+ if android_app_installed "$pkg" "$@"; then return 0; fi
154
+ run_android_native_build "Android dev client ${pkg} not installed"
155
+ ;;
156
+ rebuild-native|clean)
157
+ run_android_native_build "Rebuilding Android dev client ${pkg} (--preflight-mode ${mode})"
158
+ ;;
159
+ *)
160
+ printf 'open-device: unknown --preflight-mode: %s\n' "$mode" >&2; return 2 ;;
161
+ esac
162
+ }
163
+
164
+ # URL-encode using python3 (same as baseline).
165
+ urlencode() {
166
+ python3 - "$1" <<'PY'
167
+ import sys, urllib.parse
168
+ print(urllib.parse.quote(sys.argv[1], safe=''))
169
+ PY
170
+ }
171
+
172
+ # --- dispatch -----------------------------------------------------------------
173
+
174
+ BUNDLE_IDS=("${IOS_BUNDLE_ID:-io.metamask.MetaMask}" "io.metamask" "io.metamask.flask" "io.metamask.qa")
175
+ ANDROID_PKGS=("${ANDROID_PACKAGE_ID:-io.metamask}" "io.metamask.flask" "io.metamask.qa")
176
+
177
+ if [ "$PLATFORM" = "ios" ]; then
178
+ SIM_TARGET="$SIMULATOR"
179
+ # Expo dev-client URL scheme — drives both the deep link and the scheme-approval key.
180
+ DEV_CLIENT_SCHEME="${IOS_DEV_CLIENT_SCHEME:-expo-metamask}"
181
+
182
+ boot_simulator_if_needed "$SIM_TARGET"
183
+ show_simulator "$SIM_TARGET"
184
+
185
+ LAUNCHED=false
186
+ for BUNDLE_ID in "${BUNDLE_IDS[@]}"; do
187
+ ensure_ios_app_for_mode "$SIM_TARGET" "$BUNDLE_ID" "$PREFLIGHT_MODE" || continue
188
+
189
+ # Dismiss Expo dev-menu onboarding for clean headless runs.
190
+ xcrun simctl spawn "$SIM_TARGET" defaults write "$BUNDLE_ID" \
191
+ EXDevMenuIsOnboardingFinished -bool YES 2>/dev/null || true
192
+
193
+ # Pre-approve the dev-client deep-link scheme so openurl never pauses on the
194
+ # "Open in <App>?" dialog. The key encodes the CoreSimulator bridge + scheme;
195
+ # the value is the bundle ID of the approved handler.
196
+ xcrun simctl spawn "$SIM_TARGET" defaults write \
197
+ com.apple.launchservices.schemeapproval \
198
+ "com.apple.CoreSimulator.CoreSimulatorBridge-->${DEV_CLIENT_SCHEME}" \
199
+ -string "$BUNDLE_ID" 2>/dev/null || true
200
+
201
+ # Open via expo-development-client deep link so the app connects to our Metro.
202
+ ENCODED_URL="$(urlencode "http://localhost:${PORT}?disableOnboarding=1" 2>/dev/null \
203
+ || printf 'http%%3A%%2F%%2Flocalhost%%3A%s%%3FdisableOnboarding%%3D1' "$PORT")"
204
+ DEEP_LINK="${DEV_CLIENT_SCHEME}://expo-development-client/?url=${ENCODED_URL}"
205
+
206
+ printf 'Launching iOS dev client %s on %s\n' "$BUNDLE_ID" "$SIM_TARGET" >&2
207
+ if xcrun simctl openurl "$SIM_TARGET" "$DEEP_LINK" 2>/dev/null; then
208
+ LAUNCHED=true
209
+ printf 'open-device: iOS dev client opened (%s on %s)\n' "$BUNDLE_ID" "$SIM_TARGET" >&2
210
+ break
211
+ fi
212
+ # Fallback: plain launch (older Expo / non-development-client build).
213
+ if xcrun simctl launch "$SIM_TARGET" "$BUNDLE_ID" 2>/dev/null; then
214
+ LAUNCHED=true
215
+ printf 'open-device: iOS dev client launched (fallback) (%s on %s)\n' "$BUNDLE_ID" "$SIM_TARGET" >&2
216
+ break
217
+ fi
218
+ done
219
+
220
+ if [ "$LAUNCHED" = false ]; then
221
+ printf 'open-device: no MetaMask bundle found on simulator %s\n' "$SIM_TARGET" >&2
222
+ printf ' tried: %s\n' "${BUNDLE_IDS[*]}" >&2
223
+ exit 1
224
+ fi
225
+
226
+ else
227
+ ADB_ARGS=()
228
+ [ -n "$ADB_SERIAL_ARG" ] && ADB_ARGS+=("-s" "$ADB_SERIAL_ARG")
229
+
230
+ # Reverse-tunnel Metro port so the device can reach localhost:PORT.
231
+ adb "${ADB_ARGS[@]}" reverse "tcp:${PORT}" "tcp:${PORT}" >/dev/null 2>&1 || \
232
+ printf 'open-device: adb reverse failed (non-fatal — device may already be reachable)\n' >&2
233
+
234
+ LAUNCHED=false
235
+ for PKG in "${ANDROID_PKGS[@]}"; do
236
+ ensure_android_app_for_mode "$PKG" "$PREFLIGHT_MODE" "${ADB_ARGS[@]}" || continue
237
+
238
+ ENCODED_URL="$(urlencode "http://localhost:${PORT}?disableOnboarding=1" 2>/dev/null \
239
+ || printf 'http%%3A%%2F%%2Flocalhost%%3A%s%%3FdisableOnboarding%%3D1' "$PORT")"
240
+ DEEP_LINK="expo-metamask://expo-development-client/?url=${ENCODED_URL}"
241
+
242
+ printf 'Launching Android dev client %s\n' "$PKG" >&2
243
+ if adb "${ADB_ARGS[@]}" shell am start \
244
+ -a android.intent.action.VIEW \
245
+ -d "$DEEP_LINK" >/dev/null 2>&1; then
246
+ LAUNCHED=true
247
+ printf 'open-device: Android dev client opened (%s)\n' "$PKG" >&2
248
+ break
249
+ fi
250
+ done
251
+
252
+ if [ "$LAUNCHED" = false ]; then
253
+ printf 'open-device: no MetaMask package found on Android device %s\n' \
254
+ "${ADB_SERIAL_ARG:-<default>}" >&2
255
+ printf ' tried: %s\n' "${ANDROID_PKGS[*]}" >&2
256
+ exit 1
257
+ fi
258
+ fi
259
+
260
+ printf ' Next: mm-harness logs # tail Metro\n' >&2
@@ -7,13 +7,22 @@ up empty here by design: the feature's purpose is non-current checkouts.
7
7
 
8
8
  - Purpose: make historical product checkouts bootable/bridgeable under the
9
9
  current local toolchain so Recipe v1 evals can replay against them.
10
- - When it applies: mobile checkouts predating React Native 0.81 polyfill
11
- fixes (see mobile/README.md for the per-patch details).
10
+ - When it applies: mobile checkouts predating React Native 0.81 polyfill fixes.
12
11
  - How it is applied: MANUALLY (no automatic apply logic in this repo).
13
12
  The operator or the `/recipe-harness` skill applies the patch reversibly
14
13
  before a historical rebuild (`git apply <patch>` in the product checkout,
15
14
  `git apply -R` to remove) and records the overlay path in validation
16
- evidence. `orchestration/mobile/inject.sh` does NOT apply these.
15
+ evidence. `adapters/mobile/inject.sh` does NOT apply these.
17
16
 
18
- Listed in orchestration/manifest.json (kind: compat-overlay) so the doctor
17
+ Kept in the external runner repository, not Farmslot root scripts, so a
18
+ `/recipe-harness` skill can apply the same reversible overlay before a
19
+ historical rebuild and record the overlay path in validation evidence.
20
+
21
+ Listed in adapters/manifest.json (kind: compat-overlay) so the doctor
19
22
  verifies the surface and this directory is never flagged as unused again.
23
+
24
+ ## Patches
25
+
26
+ - `rn81-message-event-source.patch`: adds a read-only `MessageEvent.prototype.source`
27
+ getter for React Native 0.81 historical checkouts whose bundled polyfill omits
28
+ it while CDP websocket clients expect browser-compatible `MessageEvent` shape.
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ # prewarm-bundle — curl the Metro bundle URL before the dev client opens.
3
+ #
4
+ # Triggers Metro to compile and cache the bundle so the app connects instantly.
5
+ # Streams progress from the Metro log every 30s until the bundle curl finishes
6
+ # or MOBILE_BUNDLE_PREWARM_TIMEOUT seconds elapse.
7
+ # Set MOBILE_BUNDLE_PREWARM=0 to skip.
8
+ #
9
+ # Inputs:
10
+ # --platform ios|android (default ios)
11
+ # --target <metamask-mobile dir> (default $PWD)
12
+ # --port <number> (default WATCHER_PORT env, else METRO_PORT, else 8081)
13
+ # Outputs:
14
+ # Progress on stderr; exit 0 bundle ready; 1 timeout or curl error; 2 bad args.
15
+ #
16
+ # Single device op: prewarm the bundle. Does not start Metro or open the dev client.
17
+ set -uo pipefail
18
+
19
+ PLATFORM="ios"
20
+ TARGET="$PWD"
21
+ PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
22
+
23
+ while [ "$#" -gt 0 ]; do
24
+ case "$1" in
25
+ --platform) PLATFORM="$2"; shift 2 ;;
26
+ --target) TARGET="$2"; shift 2 ;;
27
+ --port) PORT="$2"; shift 2 ;;
28
+ -h|--help)
29
+ printf 'Usage: prewarm-bundle.sh [--platform ios|android] [--target <dir>] [--port <n>]\n'
30
+ exit 0
31
+ ;;
32
+ *) printf 'prewarm-bundle: unknown arg: %s\n' "$1" >&2; exit 2 ;;
33
+ esac
34
+ done
35
+
36
+ case "$PLATFORM" in
37
+ ios|android) ;;
38
+ *) printf 'prewarm-bundle: --platform must be ios or android (got: %s)\n' "$PLATFORM" >&2; exit 2 ;;
39
+ esac
40
+
41
+ TARGET="$(cd "$TARGET" && pwd)"
42
+ LOG_DIR="$TARGET/temp/recipe/runtime"
43
+ mkdir -p "$LOG_DIR"
44
+ METRO_LOG="$LOG_DIR/metro.log"
45
+
46
+ if [ "${MOBILE_BUNDLE_PREWARM:-1}" = "0" ]; then
47
+ printf 'Skipping %s bundle prewarm (MOBILE_BUNDLE_PREWARM=0)\n' "$PLATFORM" >&2
48
+ exit 0
49
+ fi
50
+
51
+ TIMEOUT="${MOBILE_BUNDLE_PREWARM_TIMEOUT:-900}"
52
+
53
+ BUNDLE_URL="http://localhost:${PORT}/index.bundle?platform=${PLATFORM}&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=1&transform.routerRoot=app&unstable_transformProfile=hermes-stable"
54
+
55
+ printf 'Prewarming %s bundle before launching dev client (set MOBILE_BUNDLE_PREWARM=0 to skip)\n' "$PLATFORM" >&2
56
+ printf ' url: %s\n' "$BUNDLE_URL" >&2
57
+
58
+ curl -sfL --connect-timeout 5 --max-time "$TIMEOUT" -o /dev/null "$BUNDLE_URL" &
59
+ BUNDLE_PID=$!
60
+
61
+ ELAPSED=0
62
+ LAST_PROGRESS=""
63
+ while kill -0 "$BUNDLE_PID" 2>/dev/null; do
64
+ PROGRESS=""
65
+ if [ -f "$METRO_LOG" ]; then
66
+ PROGRESS="$(grep -E '^[[:space:]]*(iOS|Android).*index\.(js|tsx?).*%|Bundl(ed|ing)|Finished' "$METRO_LOG" 2>/dev/null | tail -1 | tr -d '\r' || true)"
67
+ fi
68
+ if [ -n "$PROGRESS" ] && [ "$PROGRESS" != "$LAST_PROGRESS" ]; then
69
+ printf 'Bundle progress: %s\n' "$PROGRESS" >&2
70
+ LAST_PROGRESS="$PROGRESS"
71
+ elif [ $((ELAPSED % 30)) -eq 0 ] && [ "$ELAPSED" -gt 0 ]; then
72
+ printf 'Waiting for %s bundle (%ss/%ss)%s\n' "$PLATFORM" "$ELAPSED" "$TIMEOUT" \
73
+ "${LAST_PROGRESS:+ — $LAST_PROGRESS}" >&2
74
+ fi
75
+ if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
76
+ kill "$BUNDLE_PID" 2>/dev/null || true
77
+ wait "$BUNDLE_PID" 2>/dev/null || true
78
+ printf 'prewarm-bundle: %s bundle did not become ready before timeout (%ss)\n' "$PLATFORM" "$TIMEOUT" >&2
79
+ [ -f "$METRO_LOG" ] && tail -80 "$METRO_LOG" >&2 || true
80
+ printf ' Next: check %s for errors or reduce MOBILE_BUNDLE_PREWARM_TIMEOUT\n' "$METRO_LOG" >&2
81
+ exit 1
82
+ fi
83
+ sleep 3
84
+ ELAPSED=$((ELAPSED + 3))
85
+ done
86
+
87
+ if wait "$BUNDLE_PID"; then
88
+ printf '%s bundle ready\n' "$PLATFORM" >&2
89
+ exit 0
90
+ fi
91
+
92
+ printf 'prewarm-bundle: %s bundle curl failed\n' "$PLATFORM" >&2
93
+ [ -f "$METRO_LOG" ] && tail -80 "$METRO_LOG" >&2 || true
94
+ printf ' Next: check %s for Metro errors\n' "$METRO_LOG" >&2
95
+ exit 1