@deeeed/metamask-harness 0.79.0 → 0.81.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 (252) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +1 -1
  3. package/adapters/extension/ensure-browser.sh +2 -2
  4. package/adapters/extension/inject.mjs +0 -1
  5. package/adapters/extension/launch-browser.cjs +39 -613
  6. package/adapters/extension/readiness.mjs +2 -5
  7. package/adapters/extension/reattach.sh +2 -2
  8. package/adapters/extension/stamp-runtime-title.cjs +1 -1
  9. package/adapters/manifest.json +16 -64
  10. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
  11. package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
  12. package/adapters/mobile/inject.sh +16 -0
  13. package/adapters/mobile/launch-console-forwarder.cjs +1 -2
  14. package/adapters/mobile/open-device.sh +2 -2
  15. package/adapters/mobile/reload-app.mjs +4 -4
  16. package/adapters/mobile/reset-app-data.sh +1 -1
  17. package/adapters/mobile/start-console-forwarder.sh +3 -2
  18. package/adapters/mobile/start-metro.sh +17 -12
  19. package/adapters/mobile/wait-for-bridge.cjs +2 -2
  20. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  21. package/adapters/shared/open-debug.mjs +2 -2
  22. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  23. package/adapters/terminal/cleanup.mjs +2 -1
  24. package/adapters/terminal/inject.mjs +2 -1
  25. package/adapters/terminal/launch.mjs +17 -13
  26. package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
  27. package/adapters/terminal/lib/readiness.mjs +20 -12
  28. package/adapters/terminal/policy.mjs +35 -0
  29. package/adapters/terminal/verify.mjs +10 -3
  30. package/adapters/terminal/wallet-host.mjs +43 -225
  31. package/bin/mm-harness +9 -28
  32. package/dist/adapters/checkout-files.js +16 -0
  33. package/dist/adapters/console-capture.js +14 -1
  34. package/dist/adapters/core/actions.js +8 -4
  35. package/dist/adapters/core/overlay.js +1 -1
  36. package/dist/adapters/core/readiness.js +28 -1
  37. package/dist/adapters/core/run.js +2 -2
  38. package/dist/adapters/core/surface.js +16 -1
  39. package/dist/adapters/extension/actions.js +6 -2
  40. package/dist/adapters/extension/browser-pid.js +56 -0
  41. package/dist/adapters/extension/console-capture.js +1 -1
  42. package/dist/adapters/extension/doctor-checks.js +1 -1
  43. package/dist/adapters/extension/ensure-ready.js +5 -4
  44. package/dist/adapters/extension/extension-id.js +1 -1
  45. package/dist/adapters/extension/feature-flags.js +1 -1
  46. package/dist/adapters/extension/fixtures.js +1 -2
  47. package/dist/adapters/extension/launch.js +4 -5
  48. package/dist/adapters/extension/launched-browser.js +1 -2
  49. package/dist/adapters/extension/network-observer.js +14 -1
  50. package/dist/adapters/extension/overlay.js +1 -1
  51. package/dist/adapters/extension/product-config.js +1 -1
  52. package/dist/adapters/extension/readiness.js +1 -1
  53. package/dist/adapters/extension/reload.js +1 -1
  54. package/dist/adapters/extension/run.js +135 -14
  55. package/dist/adapters/extension/runtime-decision.js +2 -1
  56. package/dist/adapters/extension/runtime.js +9 -5
  57. package/dist/adapters/extension/surface.js +15 -2
  58. package/dist/adapters/extension/verify.js +2 -2
  59. package/dist/adapters/harness-freshness.js +2 -1
  60. package/dist/adapters/mobile/actions.js +4 -1
  61. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  62. package/dist/adapters/mobile/app-identity.js +10 -0
  63. package/dist/adapters/mobile/doctor-checks.js +1 -1
  64. package/dist/adapters/mobile/fixtures.js +1 -1
  65. package/dist/adapters/mobile/launch.js +27 -28
  66. package/dist/adapters/mobile/lifecycle.js +1 -1
  67. package/dist/adapters/mobile/metro-env.js +9 -59
  68. package/dist/adapters/mobile/network-observer.js +68 -0
  69. package/dist/adapters/mobile/performance-observer.js +2 -4
  70. package/dist/adapters/mobile/prepare-target.js +1 -1
  71. package/dist/adapters/mobile/prepare.js +3 -4
  72. package/dist/adapters/mobile/provision.js +1 -1
  73. package/dist/adapters/mobile/readiness.js +1 -1
  74. package/dist/adapters/mobile/recording-target.js +15 -0
  75. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  76. package/dist/adapters/mobile/reload.js +1 -1
  77. package/dist/adapters/mobile/run.js +39 -15
  78. package/dist/adapters/mobile/runtime-decision.js +2 -4
  79. package/dist/adapters/mobile/source-freshness.js +22 -126
  80. package/dist/adapters/mobile/surface.js +26 -3
  81. package/dist/adapters/mobile/ui.js +16 -5
  82. package/dist/adapters/mobile/verify.js +2 -3
  83. package/dist/adapters/performance/cdp-trace.js +1 -1
  84. package/dist/adapters/performance/device-platform.js +1 -1
  85. package/dist/adapters/perps-order-inputs.js +36 -0
  86. package/dist/adapters/proof-failure.js +11 -0
  87. package/dist/adapters/slot-ports.js +1 -1
  88. package/dist/adapters/surface.js +1 -0
  89. package/dist/adapters/terminal/actions.js +4 -1
  90. package/dist/adapters/terminal/console-capture.js +1 -1
  91. package/dist/adapters/terminal/doctor-checks.js +2 -0
  92. package/dist/adapters/terminal/launch.js +4 -1
  93. package/dist/adapters/terminal/overlay.js +8 -1
  94. package/dist/adapters/terminal/run.js +33 -5
  95. package/dist/adapters/terminal/surface.js +9 -1
  96. package/dist/adapters.js +27 -7
  97. package/dist/advisor-policy.js +1 -1
  98. package/dist/checkout-view.js +1 -2
  99. package/dist/cli.js +59 -21
  100. package/dist/command-contract.js +1 -26
  101. package/dist/commands/check.js +33 -10
  102. package/dist/commands/checklist.js +1 -1
  103. package/dist/commands/completion-candidates.js +6 -14
  104. package/dist/commands/config.js +2 -2
  105. package/dist/commands/core-readiness.js +12 -7
  106. package/dist/commands/debug.js +3 -3
  107. package/dist/commands/device-target.js +5 -5
  108. package/dist/commands/doctor.js +2 -14
  109. package/dist/commands/domain.js +2 -2
  110. package/dist/commands/ensure-ready.js +1 -7
  111. package/dist/commands/fixtures.js +5 -6
  112. package/dist/commands/help.js +3 -4
  113. package/dist/commands/launch/device.js +10 -17
  114. package/dist/commands/launch/extension.js +2 -8
  115. package/dist/commands/launch/mobile.js +1 -1
  116. package/dist/commands/logs.js +3 -3
  117. package/dist/commands/mobile-device-view.js +4 -2
  118. package/dist/commands/overlay.js +110 -0
  119. package/dist/commands/pin.js +1 -1
  120. package/dist/commands/pr-body.js +2 -2
  121. package/dist/commands/prepare.js +4 -9
  122. package/dist/commands/provision.js +3 -12
  123. package/dist/commands/recipe-advice.js +1 -2
  124. package/dist/commands/recipe-quality.js +2 -3
  125. package/dist/commands/resolve-extension.js +1 -7
  126. package/dist/commands/review.js +4 -4
  127. package/dist/commands/runtime-decision.js +1 -6
  128. package/dist/commands/runtime-health.js +1 -7
  129. package/dist/commands/runtime-launch.js +1 -13
  130. package/dist/commands/self-test.js +5 -8
  131. package/dist/commands/status-watch.js +1 -2
  132. package/dist/commands/status.js +2 -5
  133. package/dist/commands/task-init.js +2 -3
  134. package/dist/commands/tutorial.js +2 -2
  135. package/dist/commands/update.js +1 -1
  136. package/dist/completions-cache.js +1 -1
  137. package/dist/console-classifier.js +19 -0
  138. package/dist/doctor.js +2 -3
  139. package/dist/engine.js +57 -0
  140. package/dist/funding-execution-context.js +96 -1
  141. package/dist/host.js +32 -0
  142. package/dist/index.js +1 -6
  143. package/dist/live-tsx.js +49 -0
  144. package/dist/mm-harness-cli.js +4 -6
  145. package/dist/paths.js +34 -64
  146. package/dist/performance-observation.js +3 -5
  147. package/dist/progress.js +1 -1
  148. package/dist/recipe-assistance-context.js +6 -9
  149. package/dist/recipe-security.js +2 -59
  150. package/dist/review/knowledge.js +2 -2
  151. package/dist/runner.js +7 -4
  152. package/dist/runtime-context.js +1 -1
  153. package/dist/task-view.js +2 -3
  154. package/docs/CONTRIBUTING.md +5 -3
  155. package/docs/RECIPES.md +31 -3
  156. package/docs/cli-contract.md +2 -2
  157. package/docs/feature-guard.md +90 -0
  158. package/library/README.md +3 -1
  159. package/library/actions/core/perps/_controller.mjs +153 -134
  160. package/library/actions/core/perps/_rewards.mjs +186 -0
  161. package/library/actions/core/perps/close_positions.mjs +1 -1
  162. package/library/actions/core/perps/place_order.mjs +167 -45
  163. package/library/actions/core/perps/quote_fees.mjs +61 -0
  164. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  165. package/library/actions/extension/feature_flags/state.mjs +1 -1
  166. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  167. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  168. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  169. package/library/actions/extension/perps/perps.mjs +320 -3
  170. package/library/actions/extension/perps/place_order.mjs +3 -1
  171. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  172. package/library/actions/extension/platform/cdp.mjs +73 -11
  173. package/library/actions/harness-exports.mjs +1 -6
  174. package/library/actions/mobile/app/network-control.mjs +6 -8
  175. package/library/actions/mobile/perps/perps.mjs +216 -11
  176. package/library/actions/mobile/perps/place_order.mjs +3 -1
  177. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  178. package/library/actions/mobile/platform/bridge.mjs +8 -8
  179. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  180. package/library/actions/mobile/platform/native-session.mjs +3 -4
  181. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  182. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  183. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  184. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  185. package/library/actions/shared/console-findings.mjs +1 -1
  186. package/library/actions/shared/perps/order-policy.mjs +177 -0
  187. package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
  188. package/library/actions/terminal/app/launch.mjs +3 -2
  189. package/library/actions/terminal/platform/hud.mjs +2 -1
  190. package/library/actions/terminal/platform/page.mjs +2 -1
  191. package/library/actions/terminal/platform/runtime.mjs +2 -1
  192. package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
  193. package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
  194. package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
  195. package/library/manifests/core.action-manifest.json +87 -13
  196. package/library/manifests/extension.action-manifest.json +4 -0
  197. package/library/manifests/mobile.action-manifest.json +12 -0
  198. package/package.json +8 -9
  199. package/site/assets/help-recipes.json +1 -0
  200. package/adapters/extension/lib/slot-title.cjs +0 -292
  201. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  202. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  203. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  204. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  205. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  206. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  207. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  208. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  209. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  210. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  211. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  212. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  213. package/adapters/mobile/launch-metro.cjs +0 -77
  214. package/adapters/mobile/metro-config.cjs +0 -98
  215. package/adapters/mobile/metro-log-generation.cjs +0 -106
  216. package/adapters/terminal/lib/origin.mjs +0 -30
  217. package/adapters/terminal/lib/page-script.mjs +0 -246
  218. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  219. package/dist/adapters/mobile/frame-metrics.js +0 -118
  220. package/dist/adapters/mobile/video-recorder.js +0 -289
  221. package/dist/artifact-files.js +0 -92
  222. package/dist/checkout-lock.js +0 -149
  223. package/dist/cli-color.js +0 -98
  224. package/dist/command-journal.js +0 -308
  225. package/dist/commands/call.js +0 -685
  226. package/dist/commands/last.js +0 -60
  227. package/dist/commands/launch/index.js +0 -109
  228. package/dist/commands/list-executables.js +0 -247
  229. package/dist/commands/manifest.js +0 -488
  230. package/dist/commands/parse-args.js +0 -279
  231. package/dist/commands/reload.js +0 -41
  232. package/dist/commands/run-engine.js +0 -1340
  233. package/dist/commands/run-report.js +0 -241
  234. package/dist/commands/run.js +0 -751
  235. package/dist/commands/shared.js +0 -323
  236. package/dist/commands/stop.js +0 -60
  237. package/dist/devices.js +0 -100
  238. package/dist/discovery.js +0 -15
  239. package/dist/execution-provenance.js +0 -332
  240. package/dist/harness.js +0 -451
  241. package/dist/heal-bounds.js +0 -137
  242. package/dist/json-stream.js +0 -60
  243. package/dist/leaf-invoke.js +0 -19
  244. package/dist/library-provenance.js +0 -33
  245. package/dist/live-adapter-contract.js +0 -380
  246. package/dist/metamask-action-validation.js +0 -68
  247. package/dist/network-observation.js +0 -290
  248. package/dist/recording-target.js +0 -120
  249. package/dist/run-diagnostics.js +0 -364
  250. package/dist/run-recording.js +0 -481
  251. package/dist/runtime-proof.js +0 -90
  252. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
@@ -1,5 +1,5 @@
1
+ import { EXIT, spawnScriptStreaming } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
- import { EXIT, spawnScriptStreaming } from "../../commands/shared.js";
3
3
  import { runnerDir } from "../../paths.js";
4
4
  import { FIXTURE_STREAM_TIMEOUT_MS } from "../../commands/fixtures.js";
5
5
  const FIXTURE_BRIDGE_READY_TIMEOUT_MS = 18e4;
@@ -1,10 +1,9 @@
1
1
  import fs from "node:fs";
2
+ import { recipeRuntimePath, EXIT, str, writeInteractiveProgress } from "@farmslot/recipe-cli/harness";
2
3
  import { applyDeviceTargeting } from "../../commands/device-target.js";
3
4
  import { launchMobile } from "../../commands/launch/mobile.js";
4
5
  import { shellQuote } from "../../commands/launch/quote.js";
5
- import { EXIT, str, writeInteractiveProgress } from "../../commands/shared.js";
6
- import { RECOVERY_CODE } from "../../heal-bounds.js";
7
- import { recipeRuntimePath } from "../../paths.js";
6
+ const METRO_RESTARTED = "metro.restarted";
8
7
  function mobileProvisioningBlocked(output) {
9
8
  return /open-device: configured iOS simulator '.+' does not exist|open-device: no MetaMask bundle found|fast mode requires an installed (?:iOS dev client|Android dev client)/u.test(output);
10
9
  }
@@ -17,8 +16,8 @@ function ambiguousConfiguredIosSimulator(output) {
17
16
  function mobileBridgeTargetMissing(output) {
18
17
  return /no bridge target matched|wait-for-bridge: readiness deadline expired/iu.test(output);
19
18
  }
20
- function mobileProvisionCommand(target, mobileTarget) {
21
- const platform = mobileTarget === "android" ? "android" : "ios";
19
+ function mobileProvisionCommand(target, platformTarget) {
20
+ const platform = platformTarget === "android" ? "android" : "ios";
22
21
  const device = platform === "ios" ? process.env.SIM_UDID || process.env.IOS_SIMULATOR : process.env.ADB_SERIAL || process.env.ANDROID_SERIAL || process.env.ANDROID_DEVICE;
23
22
  const parts = ["mm-harness", "provision", "runway", platform, "--adapter", "mobile", "--target", shellQuote(target)];
24
23
  if (device) parts.push("--device", shellQuote(device));
@@ -27,11 +26,11 @@ function mobileProvisionCommand(target, mobileTarget) {
27
26
  function isIosSimulatorUdid(value) {
28
27
  return /^[0-9A-Fa-f-]{36}$/u.test(value);
29
28
  }
30
- function launchDeviceLabel(mobileTarget) {
31
- if (mobileTarget === "android") {
29
+ function launchDeviceLabel(platformTarget) {
30
+ if (platformTarget === "android") {
32
31
  return process.env.ANDROID_TARGET_DEVICE_NAME || process.env.ADB_SERIAL || process.env.ANDROID_SERIAL || process.env.ANDROID_DEVICE || "android device";
33
32
  }
34
- if (mobileTarget === "ios") {
33
+ if (platformTarget === "ios") {
35
34
  return process.env.IOS_SIMULATOR || process.env.SIM_UDID || "iOS simulator";
36
35
  }
37
36
  return process.env.IOS_SIMULATOR || process.env.ADB_SERIAL || "booted device";
@@ -51,22 +50,22 @@ function metroLaunchEvidence(target) {
51
50
  }
52
51
  }
53
52
  const mobileLaunchProfile = {
54
- refuseFlags({ mobileTarget }) {
55
- if (!mobileTarget) {
53
+ refuseFlags({ platformTarget }) {
54
+ if (!platformTarget) {
56
55
  return { message: "target is required for mobile.", userAction: "mm-harness launch ios or mm-harness launch android" };
57
56
  }
58
57
  return void 0;
59
58
  },
60
- applyEnv({ adapter, options, mobileTarget, target }) {
59
+ applyEnv({ adapter, options, platformTarget, target }) {
61
60
  const device = str(options, "device");
62
- if (!device && mobileTarget !== "ios" && mobileTarget !== "android") return void 0;
61
+ if (!device && platformTarget !== "ios" && platformTarget !== "android") return void 0;
63
62
  const targetingOptions = {
64
63
  ...options,
65
- ...mobileTarget ? { platform: mobileTarget } : {}
64
+ ...platformTarget ? { platform: platformTarget } : {}
66
65
  };
67
66
  const result = applyDeviceTargeting("launch", adapter, targetingOptions, { gate: false, rerun: "" });
68
67
  if ("code" in result) {
69
- if (mobileTarget === "ios" && result.code === "DEVICE_NOT_FOUND") {
68
+ if (platformTarget === "ios" && result.code === "DEVICE_NOT_FOUND") {
70
69
  process.env.IOS_SIMULATOR = device;
71
70
  process.env.SIM_UDID = isIosSimulatorUdid(device) ? device : "";
72
71
  process.env.ADB_SERIAL = "";
@@ -77,7 +76,7 @@ const mobileLaunchProfile = {
77
76
  return result;
78
77
  }
79
78
  }
80
- if (mobileTarget === "android" && device && !process.env.ADB_SERIAL && !process.env.ANDROID_SERIAL) {
79
+ if (platformTarget === "android" && device && !process.env.ADB_SERIAL && !process.env.ANDROID_SERIAL) {
81
80
  return {
82
81
  ok: false,
83
82
  code: "DEVICE_WRONG_PLATFORM",
@@ -85,7 +84,7 @@ const mobileLaunchProfile = {
85
84
  userAction: `mm-harness status --target ${shellQuote(target)} --all-devices --json`
86
85
  };
87
86
  }
88
- if (mobileTarget === "ios" && device && !process.env.IOS_SIMULATOR) {
87
+ if (platformTarget === "ios" && device && !process.env.IOS_SIMULATOR) {
89
88
  return {
90
89
  ok: false,
91
90
  code: "DEVICE_WRONG_PLATFORM",
@@ -95,17 +94,17 @@ const mobileLaunchProfile = {
95
94
  }
96
95
  return void 0;
97
96
  },
98
- start({ options, mobileTarget, machine, tier, wantClearMetro }) {
99
- const device = str(options, "device") ?? (mobileTarget === "android" ? process.env.ADB_SERIAL ?? process.env.ANDROID_SERIAL : process.env.IOS_SIMULATOR ?? process.env.SIM_UDID) ?? "configured device";
97
+ start({ options, platformTarget, machine, tier, wantClearMetro }) {
98
+ const device = str(options, "device") ?? (platformTarget === "android" ? process.env.ADB_SERIAL ?? process.env.ANDROID_SERIAL : process.env.IOS_SIMULATOR ?? process.env.SIM_UDID) ?? "configured device";
100
99
  const watcherPort = str(options, "watcherPort") ?? process.env.WATCHER_PORT ?? "auto";
101
100
  writeInteractiveProgress(
102
101
  machine,
103
- `\u2192 mobile launch \u2014 ${mobileTarget} \xB7 ${tier === "build" ? "native build" : "quick readiness"} \xB7 ${device} \xB7 Metro ${watcherPort === "auto" ? "auto" : `:${watcherPort}`}${wantClearMetro ? " \xB7 clear cache" : ""}`
102
+ `\u2192 mobile launch \u2014 ${platformTarget} \xB7 ${tier === "build" ? "native build" : "quick readiness"} \xB7 ${device} \xB7 Metro ${watcherPort === "auto" ? "auto" : `:${watcherPort}`}${wantClearMetro ? " \xB7 clear cache" : ""}`
104
103
  );
105
104
  },
106
105
  nativeInputs: ["ios", "android", "package.json", "yarn.lock"],
107
- rebuildCommand: ({ mobileTarget }) => `mm-harness launch ${mobileTarget} --build`,
108
- compose: ({ target, mobileTarget, machine, remoteFlagRequest }, tier, restartApp, clearMetro) => launchMobile(target, mobileTarget, tier, machine, restartApp, clearMetro, remoteFlagRequest?.flags),
106
+ rebuildCommand: ({ platformTarget }) => `mm-harness launch ${platformTarget} --build`,
107
+ compose: ({ target, platformTarget, machine, remoteFlagRequest }, tier, restartApp, clearMetro) => launchMobile(target, platformTarget, tier, machine, restartApp, clearMetro, remoteFlagRequest?.flags),
109
108
  classify(_launch, output) {
110
109
  if (output.includes("open-device: Android device is locked.")) {
111
110
  return {
@@ -130,7 +129,7 @@ const mobileLaunchProfile = {
130
129
  return void 0;
131
130
  },
132
131
  async beforeHeal(launch, attempt, state) {
133
- const { target, mobileTarget, tier } = launch;
132
+ const { target, platformTarget, tier } = launch;
134
133
  const output = attempt.output;
135
134
  const missingIosSimulator = missingConfiguredIosSimulator(output);
136
135
  const ambiguousIosSimulator = ambiguousConfiguredIosSimulator(output);
@@ -157,9 +156,9 @@ const mobileLaunchProfile = {
157
156
  if (mobileProvisioningBlocked(output)) {
158
157
  return launch.fail(state, {
159
158
  code: "MOBILE_PROVISION_REQUIRED",
160
- message: `mobile ${mobileTarget ?? "runtime"} is not provisioned for this slot.`,
159
+ message: `mobile ${platformTarget ?? "runtime"} is not provisioned for this slot.`,
161
160
  recoverable: false,
162
- userAction: mobileProvisionCommand(target, mobileTarget),
161
+ userAction: mobileProvisionCommand(target, platformTarget),
163
162
  exitCode: EXIT.runtime,
164
163
  originalError: output.trim() || void 0
165
164
  });
@@ -177,14 +176,14 @@ const mobileLaunchProfile = {
177
176
  },
178
177
  recovery(_launch, output) {
179
178
  const restartApp = mobileBridgeTargetMissing(output);
180
- return { code: restartApp ? "mobile.app-restarted" : RECOVERY_CODE.mobile, restartApp };
179
+ return { code: restartApp ? "mobile.app-restarted" : METRO_RESTARTED, restartApp };
181
180
  },
182
181
  // Mobile proved its set inside launchMobile (null: nothing requested and no
183
182
  // runtime answered).
184
183
  provenFlags: (_launch, requested, proven) => ({ flags: proven === void 0 ? requested : proven }),
185
- verifyArgs: ({ mobileTarget }) => mobileTarget ? ["--platform", mobileTarget] : [],
186
- platformLabel: ({ mobileTarget }) => mobileTarget ?? null,
187
- deviceLabel: ({ mobileTarget }) => launchDeviceLabel(mobileTarget),
184
+ verifyArgs: ({ platformTarget }) => platformTarget ? ["--platform", platformTarget] : [],
185
+ platformLabel: ({ platformTarget }) => platformTarget ?? null,
186
+ deviceLabel: ({ platformTarget }) => launchDeviceLabel(platformTarget),
188
187
  evidence: metroLaunchEvidence
189
188
  };
190
189
  export {
@@ -3,7 +3,7 @@ import {
3
3
  bridgeCommand,
4
4
  MOBILE_BRIDGE_ERROR_CODES
5
5
  } from "../../../library/actions/mobile/platform/bridge.mjs";
6
- import { resolveMobileToolPath } from "../../../library/actions/mobile/platform/tool-paths.mjs";
6
+ import { resolveMobileToolPath } from "@farmslot/adapter-rn";
7
7
  import {
8
8
  MOBILE_LIFECYCLE_DEADLINE_FIELD,
9
9
  MOBILE_LIFECYCLE_PREPARED
@@ -1,68 +1,18 @@
1
- import { createHash } from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { recipeRuntimePath } from "../../paths.js";
5
- const ENV_INPUTS = [".js.env", ".env", ".env.local"];
6
- const PROCESS_ENV_INPUTS = [
7
- "SEGMENT_PROXY_URL",
8
- "SEGMENT_WRITE_KEY",
9
- "SEGMENT_FLUSH_INTERVAL",
10
- "SEGMENT_FLUSH_EVENT_LIMIT"
11
- ];
1
+ import { metroEnvCheck, metroEnvFingerprint, recordMetroEnvBaseline } from "@farmslot/adapter-rn";
2
+ import { recipeRuntimePath } from "@farmslot/recipe-cli/harness";
3
+ const MOBILE_METRO_ENV_INPUTS = {
4
+ files: [".js.env", ".env", ".env.local"],
5
+ env: ["SEGMENT_PROXY_URL", "SEGMENT_WRITE_KEY", "SEGMENT_FLUSH_INTERVAL", "SEGMENT_FLUSH_EVENT_LIMIT"]
6
+ };
12
7
  const BASELINE_FILE = "metro-env-baseline.json";
13
8
  function mobileMetroEnvFingerprint(target) {
14
- const hash = createHash("sha256");
15
- for (const relative of ENV_INPUTS) {
16
- const absolute = path.join(target, relative);
17
- hash.update(`${relative}\0`);
18
- if (fs.existsSync(absolute)) hash.update(fs.readFileSync(absolute));
19
- else hash.update("<absent>");
20
- hash.update("\0");
21
- }
22
- for (const name of PROCESS_ENV_INPUTS) {
23
- hash.update(`${name}\0${process.env[name] ?? "<absent>"}\0`);
24
- }
25
- return hash.digest("hex");
9
+ return metroEnvFingerprint(target, MOBILE_METRO_ENV_INPUTS);
26
10
  }
27
11
  function mobileMetroEnvCheck(target) {
28
- const fingerprint = mobileMetroEnvFingerprint(target);
29
- const marker = readBaseline(target);
30
- return {
31
- fingerprint,
32
- status: marker === null ? "missing" : marker.fingerprint === fingerprint ? "current" : "changed"
33
- };
12
+ return metroEnvCheck(target, MOBILE_METRO_ENV_INPUTS, recipeRuntimePath(target, BASELINE_FILE));
34
13
  }
35
14
  function recordMobileMetroEnvBaseline(target, expectedFingerprint) {
36
- if (mobileMetroEnvFingerprint(target) !== expectedFingerprint) return false;
37
- const marker = recipeRuntimePath(target, BASELINE_FILE);
38
- const temporary = `${marker}.${process.pid}.${Date.now()}.tmp`;
39
- const baseline = {
40
- schemaVersion: 1,
41
- fingerprint: expectedFingerprint,
42
- recordedAt: (/* @__PURE__ */ new Date()).toISOString()
43
- };
44
- fs.mkdirSync(path.dirname(marker), { recursive: true });
45
- try {
46
- fs.writeFileSync(temporary, `${JSON.stringify(baseline, null, 2)}
47
- `, { mode: 384 });
48
- if (mobileMetroEnvFingerprint(target) !== expectedFingerprint) return false;
49
- fs.renameSync(temporary, marker);
50
- if (mobileMetroEnvFingerprint(target) !== expectedFingerprint) {
51
- if (readBaseline(target)?.fingerprint === expectedFingerprint) fs.rmSync(marker, { force: true });
52
- return false;
53
- }
54
- } finally {
55
- fs.rmSync(temporary, { force: true });
56
- }
57
- return true;
58
- }
59
- function readBaseline(target) {
60
- try {
61
- const parsed = JSON.parse(fs.readFileSync(recipeRuntimePath(target, BASELINE_FILE), "utf8"));
62
- return parsed.schemaVersion === 1 && typeof parsed.fingerprint === "string" ? parsed : null;
63
- } catch {
64
- return null;
65
- }
15
+ return recordMetroEnvBaseline(target, MOBILE_METRO_ENV_INPUTS, recipeRuntimePath(target, BASELINE_FILE), expectedFingerprint);
66
16
  }
67
17
  export {
68
18
  mobileMetroEnvCheck,
@@ -0,0 +1,68 @@
1
+ import path from "node:path";
2
+ import brokerModule from "@farmslot/recipe-runner/cdp-broker";
3
+ import configModule from "@farmslot/adapter-rn/bridge-runtime/lib/config.cjs";
4
+ const { brokerSocketPath, createBrokerClient } = brokerModule;
5
+ const { resolvePort } = configModule;
6
+ async function createMobileNetworkBackend(target, env) {
7
+ const { client } = await connectMobileBroker(target, env);
8
+ return {
9
+ start(params) {
10
+ return client.control("capture-start", params, 1e4);
11
+ },
12
+ async end(id) {
13
+ return asRecord(await client.control("capture-end", { id }, 1e4));
14
+ },
15
+ async close() {
16
+ client.close();
17
+ }
18
+ };
19
+ }
20
+ async function connectMobileBroker(target, env) {
21
+ const runtimeDir = env.RECIPE_RUNTIME_DIR ? path.resolve(target, env.RECIPE_RUNTIME_DIR) : path.join(target, "temp", "recipe", "runtime");
22
+ const socketPath = brokerSocketPath(
23
+ runtimeDir,
24
+ resolvePort(env, target)
25
+ );
26
+ const discoveryClient = await createBrokerClient(socketPath, "", 1e4);
27
+ let targets;
28
+ try {
29
+ targets = await discoveryClient.control(
30
+ "resolve-targets",
31
+ { nameIncludes: mobileTargetPin(env) },
32
+ 1e4
33
+ );
34
+ } finally {
35
+ discoveryClient.close();
36
+ }
37
+ const deviceId = selectMobileBrokerTarget(targets, env);
38
+ const client = await createBrokerClient(socketPath, deviceId, 1e4);
39
+ return { client, deviceId };
40
+ }
41
+ function selectMobileBrokerTarget(value, env) {
42
+ const targets = Array.isArray(value) ? value.map(asRecord).filter((target) => target.deviceId) : [];
43
+ const pin = mobileTargetPin(env);
44
+ const candidates = pin ? targets.filter(
45
+ (target) => String(target.name ?? "").toLowerCase().includes(pin.toLowerCase())
46
+ ) : targets;
47
+ if (candidates.length !== 1) {
48
+ throw new Error(
49
+ `Mobile network observation requires one broker target; found ${candidates.length}.`
50
+ );
51
+ }
52
+ return String(candidates[0].deviceId);
53
+ }
54
+ function mobileTargetPin(env) {
55
+ const platform = String(
56
+ env.RECIPE_RN_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
57
+ ).toLowerCase();
58
+ return String(
59
+ platform === "android" ? env.ANDROID_TARGET_DEVICE_NAME ?? env.ANDROID_DEVICE ?? "" : platform === "ios" ? env.IOS_SIMULATOR ?? "" : env.IOS_SIMULATOR ?? env.ANDROID_TARGET_DEVICE_NAME ?? env.ANDROID_DEVICE ?? ""
60
+ ).trim();
61
+ }
62
+ function asRecord(value) {
63
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
64
+ }
65
+ export {
66
+ connectMobileBroker,
67
+ createMobileNetworkBackend
68
+ };
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  createCdpTraceCollector
3
3
  } from "../performance/cdp-trace.js";
4
- import {
5
- connectMobileBroker
6
- } from "../../network-observation.js";
4
+ import { connectMobileBroker } from "./network-observer.js";
7
5
  import { createAndroidGfxinfoPerformanceBackend } from "./android-gfxinfo-performance.js";
8
6
  async function createMobilePerformanceBackend(target, env) {
9
7
  if (resolvePlatform(env) === "android") {
@@ -37,7 +35,7 @@ async function markRuntime(client, name) {
37
35
  }
38
36
  function resolvePlatform(env) {
39
37
  const explicit = String(
40
- env.MM_HARNESS_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
38
+ env.RECIPE_RN_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
41
39
  ).toLowerCase();
42
40
  if (explicit === "android" || explicit === "ios") return explicit;
43
41
  return env.ADB_SERIAL || env.ANDROID_SERIAL || env.ANDROID_DEVICE ? "android" : "ios";
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { recipeRuntimeDir } from "../../paths.js";
3
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
4
4
  function readRuntimeContextTarget(target) {
5
5
  const file = path.join(target, recipeRuntimeDir(), "agentic-runtime.json");
6
6
  let parsed;
@@ -1,9 +1,8 @@
1
+ import { EXIT, spawnScriptStreaming, shellQuote } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import path from "node:path";
3
4
  import { recordDepsBaseline } from "@farmslot/recipe-runner/runtime/deps-readiness";
4
- import { resolveAndExportMobileToolPaths } from "../../../library/actions/mobile/platform/tool-paths.mjs";
5
- import { EXIT, spawnScriptStreaming } from "../../commands/shared.js";
6
- import { shellQuote } from "../../commands/parse-args.js";
5
+ import { resolveAndExportMobileToolPaths } from "@farmslot/adapter-rn";
7
6
  import { runnerDir } from "../../paths.js";
8
7
  import {
9
8
  decideMobileReadiness,
@@ -401,7 +400,7 @@ async function dispatchAction(action, target, platform, json, preflightMode = "f
401
400
  leaf,
402
401
  withWatcherPort(["--target", cwd, ...extra], watcherPort),
403
402
  target,
404
- { MM_HARNESS_METRO_ENV_FINGERPRINT: metroEnvFingerprint ?? mobileMetroEnvCheck(path.resolve(target)).fingerprint }
403
+ { RECIPE_RN_METRO_ENV_FINGERPRINT: metroEnvFingerprint ?? mobileMetroEnvCheck(path.resolve(target)).fingerprint }
405
404
  );
406
405
  }
407
406
  case "start-console-forwarder": {
@@ -1,9 +1,9 @@
1
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import crypto from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import os from "node:os";
5
6
  import path from "node:path";
6
- import { recipeRuntimeDir } from "../../paths.js";
7
7
  import { resolveSlotPortsByRepo } from "../../../adapters/shared/resolve-slot-ports-core.mjs";
8
8
  const RUNWAY_IOS_METADATA = {
9
9
  artifactName: "ios-app-main-dev-expo",
@@ -1,4 +1,4 @@
1
- import { optionFlag, optionString, parsePort } from "../../commands/parse-args.js";
1
+ import { optionFlag, optionString, parsePort } from "@farmslot/recipe-cli/harness";
2
2
  import { detectOrphanMetros, mobileToolDoctorChecks } from "./doctor-checks.js";
3
3
  import { ensureMobilePerpsEnvironment, mobilePerpsEnvironment } from "./perps-env.js";
4
4
  const mobileReadiness = {
@@ -0,0 +1,15 @@
1
+ function resolveAndroidDeviceSerial(env = process.env) {
2
+ return env.ADB_SERIAL?.trim() || process.env.ADB_SERIAL?.trim() || void 0;
3
+ }
4
+ function resolveMobileIosSimulatorName(env = process.env) {
5
+ const explicit = env.IOS_SIMULATOR?.trim() || env.SIM_UDID?.trim();
6
+ if (explicit) return explicit;
7
+ const slot = env.FARMSLOT_SLOT_ID?.trim() || env.SLOT_ID?.trim();
8
+ if (!slot) return void 0;
9
+ const suffix = slot.includes("-") ? slot.slice(slot.lastIndexOf("-") + 1) : slot;
10
+ return suffix || void 0;
11
+ }
12
+ export {
13
+ resolveAndroidDeviceSerial,
14
+ resolveMobileIosSimulatorName
15
+ };
@@ -1,7 +1,7 @@
1
+ import { recipeRuntimePath } from "@farmslot/recipe-cli/harness";
1
2
  import { randomBytes } from "node:crypto";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
- import { recipeRuntimePath } from "../../paths.js";
5
5
  import {
6
6
  assertAndroidReleaseArtifactRuntime
7
7
  } from "./release-artifact.js";
@@ -1,5 +1,5 @@
1
+ import { EXIT, spawnScript } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
- import { EXIT, spawnScript } from "../../commands/shared.js";
3
3
  import { runnerDir } from "../../paths.js";
4
4
  function reloadMobile(target, json) {
5
5
  const script = path.join(runnerDir, "adapters/mobile/reload-app.mjs");
@@ -1,17 +1,19 @@
1
+ import { EXIT, isRecord } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import path from "node:path";
3
- import { isRecord } from "../../commands/parse-args.js";
4
+ import { IOS_SIMULATOR_RECORDING_TARGET_PREFIX } from "@farmslot/adapter-rn";
4
5
  import { runnerDir } from "../../paths.js";
5
6
  import { devicePlatformFromEnv } from "../performance/device-platform.js";
6
7
  import {
7
- IOS_SIMULATOR_RECORDING_TARGET_PREFIX,
8
8
  resolveAndroidDeviceSerial,
9
9
  resolveMobileIosSimulatorName
10
- } from "../../recording-target.js";
10
+ } from "./recording-target.js";
11
+ import { emitProofFailure } from "../proof-failure.js";
12
+ import { mobileAppIdentityEnv } from "./app-identity.js";
11
13
  import { requireCurrentMobileReleaseArtifactState } from "./release-artifact-state.js";
12
14
  const EXPLICIT_DEVICE_KEYS = [
13
15
  "PLATFORM",
14
- "MM_HARNESS_EXPLICIT_PLATFORM",
16
+ "RECIPE_RN_EXPLICIT_PLATFORM",
15
17
  "IOS_SIMULATOR",
16
18
  "SIM_UDID",
17
19
  "ADB_SERIAL",
@@ -21,7 +23,7 @@ const EXPLICIT_DEVICE_KEYS = [
21
23
  ];
22
24
  const mobileRun = {
23
25
  pinnedEnv() {
24
- if (!process.env.MM_HARNESS_EXPLICIT_PLATFORM) return void 0;
26
+ if (!process.env.RECIPE_RN_EXPLICIT_PLATFORM) return void 0;
25
27
  return Object.fromEntries(EXPLICIT_DEVICE_KEYS.map((key) => [key, process.env[key]]));
26
28
  },
27
29
  envKeys: ["METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME", "ANDROID_PACKAGE_ID", "ADB_SERIAL", "ANDROID_SERIAL"],
@@ -49,8 +51,11 @@ const mobileRun = {
49
51
  ANDROID_TARGET_DEVICE_NAME: process.env.ANDROID_TARGET_DEVICE_NAME,
50
52
  ADB_SERIAL: process.env.ADB_SERIAL,
51
53
  ANDROID_SERIAL: process.env.ANDROID_SERIAL,
52
- ANDROID_PACKAGE_ID: process.env.ANDROID_PACKAGE_ID,
53
- METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME: process.env.METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME
54
+ ...mobileAppIdentityEnv(),
55
+ METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME: process.env.METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME,
56
+ // The platform `--platform`/`--device` pinned; the React Native bridge
57
+ // matches its target against it.
58
+ RECIPE_RN_EXPLICIT_PLATFORM: process.env.RECIPE_RN_EXPLICIT_PLATFORM
54
59
  }),
55
60
  // An installed release artifact is opaque: no harness HUD inside it.
56
61
  autoHud: () => process.env.METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME !== "1",
@@ -64,12 +69,28 @@ const mobileRun = {
64
69
  // source only matters for a development build the caller did not restart.
65
70
  runtimeCheck({ target, heal, state, json, appRestartAuthored }) {
66
71
  if (requireCurrentMobileReleaseArtifactState(target) !== null || appRestartAuthored) return void 0;
67
- return async () => {
68
- const { ensureMobileProofRuntime } = await import("../../commands/run-engine.js");
69
- return ensureMobileProofRuntime(target, heal, state, json);
70
- };
72
+ return () => ensureMobileProofRuntime(target, heal, state, json);
71
73
  }
72
74
  };
75
+ async function ensureMobileProofRuntime(target, _heal, state, json, deps = {}) {
76
+ const source = await import("./source-freshness.js");
77
+ const check = deps.check ?? (() => source.mobileSourceCheck(target));
78
+ const initial = check();
79
+ if (initial.status === "unavailable") {
80
+ emitProofFailure(json, { recoverable: false, error: initial.error });
81
+ return EXIT.usage;
82
+ }
83
+ if (initial.status === "current") return null;
84
+ const targetArg = JSON.stringify(path.resolve(target));
85
+ const userAction = `mm-harness call app.lifecycle --arg command=restart --adapter mobile --target ${targetArg}`;
86
+ const message = `Mobile loaded source is not current (${initial.status}).`;
87
+ emitProofFailure(json, {
88
+ recoverable: true,
89
+ mutations: state.mutations,
90
+ error: { code: "MOBILE_SOURCE_NOT_LOADED", message, userAction }
91
+ });
92
+ return EXIT.infra;
93
+ }
73
94
  const mobileRecording = {
74
95
  target: (context) => resolveMobileRecordingTarget(context),
75
96
  // An Android device records through the mirror, with screenrecord as the
@@ -78,7 +99,7 @@ const mobileRecording = {
78
99
  const androidSerial = resolveAndroidDeviceSerial();
79
100
  const iosSimulator = androidSerial ? void 0 : resolveMobileIosSimulatorName();
80
101
  if (!androidSerial && !iosSimulator) return void 0;
81
- const { createAndroidVideoRecorder, createIosSimulatorVideoRecorder } = await import("./video-recorder.js");
102
+ const { createAndroidVideoRecorder, createIosSimulatorVideoRecorder } = await import("@farmslot/adapter-rn");
82
103
  if (androidSerial) {
83
104
  const { createAndroidMirrorVideoRecorder } = await import("@farmslot/recipe-runner");
84
105
  return createAndroidMirrorVideoRecorder({ serial: androidSerial, fallback: createAndroidVideoRecorder(androidSerial) });
@@ -126,11 +147,13 @@ const mobileDiagnostics = {
126
147
  };
127
148
  const mobileObservation = {
128
149
  network: {
129
- backend: async (target, env) => (await import("../../network-observation.js")).createMobileNetworkBackend(target, env)
150
+ backend: async (target, env) => (await import("./network-observer.js")).createMobileNetworkBackend(target, env)
130
151
  },
131
152
  performance: {
132
- backend: async (target, env) => (await import("./performance-observer.js")).createMobilePerformanceBackend(target, env),
133
- platform: (env) => devicePlatformFromEnv(env)
153
+ start: async (context) => (await import("../../performance-observation.js")).startRunPerformanceObservation({
154
+ backend: async (target, env) => (await import("./performance-observer.js")).createMobilePerformanceBackend(target, env),
155
+ platform: (env) => devicePlatformFromEnv(env)
156
+ }, context)
134
157
  }
135
158
  };
136
159
  function runMobileIssueCommand(projectRoot, command) {
@@ -149,6 +172,7 @@ function runMobileIssueCommand(projectRoot, command) {
149
172
  }
150
173
  }
151
174
  export {
175
+ ensureMobileProofRuntime,
152
176
  mobileDiagnostics,
153
177
  mobileObservation,
154
178
  mobileRecording,
@@ -11,10 +11,8 @@ import {
11
11
  supersededErrorCapture
12
12
  } from "@farmslot/recipe-runner/runtime/log-analysis";
13
13
  import { probeMetroPackager } from "@farmslot/recipe-runner/runtime/metro-probe";
14
- import { color } from "../../cli-color.js";
15
- import { shellQuote } from "../../commands/parse-args.js";
16
- import { recipeRuntimePath } from "../../paths.js";
17
- import { resolveMobileToolPath } from "../../../library/actions/mobile/platform/tool-paths.mjs";
14
+ import { color, recipeRuntimePath, shellQuote } from "@farmslot/recipe-cli/harness";
15
+ import { resolveMobileToolPath } from "@farmslot/adapter-rn";
18
16
  import { mobileProductMarkers } from "./deps-markers.js";
19
17
  import { mobileMetroEnvCheck } from "./metro-env.js";
20
18
  const BUNDLE_ERR = /Bundling failed|Unable to resolve /u;