@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,4 +1,5 @@
1
1
  import { manifestPath } from "../../paths.js";
2
+ import { perpsOrderInputFindings } from "../perps-order-inputs.js";
2
3
  import { extensionActionInputFindings } from "./action-inputs.js";
3
4
  const extensionActions = {
4
5
  manifestPath: () => manifestPath("extension"),
@@ -43,8 +44,11 @@ const extensionActions = {
43
44
  })
44
45
  }),
45
46
  trustedMutation: true,
46
- inputFindings: extensionActionInputFindings,
47
- ensureChecksOnly: true
47
+ // ensure_positions and ensure_orders only check state here.
48
+ inputFindings: (nodeId, node) => [
49
+ ...extensionActionInputFindings(nodeId, node),
50
+ ...perpsOrderInputFindings(nodeId, node, { ensureChecksOnly: true })
51
+ ]
48
52
  };
49
53
  export {
50
54
  extensionActions
@@ -0,0 +1,56 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ function resolveExtensionBrowserPid(projectRoot, artifactsDir, cdpPort) {
5
+ const explicit = parsePositivePid(process.env.METAMASK_RECIPE_EXTENSION_BROWSER_PID);
6
+ if (explicit) return explicit;
7
+ if (cdpPort) {
8
+ const lsof = spawnSync("lsof", ["-nP", `-iTCP:${cdpPort}`, "-sTCP:LISTEN", "-t"], {
9
+ cwd: projectRoot,
10
+ encoding: "utf8"
11
+ });
12
+ if (lsof.status === 0) {
13
+ const pid = parsePositivePid(String(lsof.stdout).split(/\s+/u).find(Boolean));
14
+ if (pid) return pid;
15
+ }
16
+ }
17
+ for (const file of [
18
+ path.join(artifactsDir, "extension-runtime/runtime.json"),
19
+ path.join(projectRoot, "temp/recipe/runtime/runtime.json")
20
+ ]) {
21
+ const pid = parsePositivePid(readJsonFile(file)?.pid);
22
+ if (pid) return pid;
23
+ }
24
+ for (const file of [
25
+ path.join(projectRoot, "temp/recipe/runtime/chromium.pid"),
26
+ path.join(projectRoot, "temp/recipe/runtime/browser.pid")
27
+ ]) {
28
+ const pid = parsePositivePid(readTextFile(file));
29
+ if (pid) return pid;
30
+ }
31
+ return null;
32
+ }
33
+ function parsePositivePid(value) {
34
+ const pid = Number(String(value ?? "").trim());
35
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
36
+ }
37
+ function readJsonFile(file) {
38
+ if (!fs.existsSync(file)) return null;
39
+ try {
40
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
41
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function readTextFile(file) {
47
+ if (!fs.existsSync(file)) return "";
48
+ try {
49
+ return fs.readFileSync(file, "utf8");
50
+ } catch {
51
+ return "";
52
+ }
53
+ }
54
+ export {
55
+ resolveExtensionBrowserPid
56
+ };
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
+ import { recipeRuntimePath } from "@farmslot/recipe-cli/harness";
2
3
  import { verifyCapture } from "../../../library/actions/shared/console-collector.mjs";
3
- import { recipeRuntimePath } from "../../paths.js";
4
4
  import { ensureConsoleCollector } from "../console-capture.js";
5
5
  function extensionCaptureFiles(target) {
6
6
  return {
@@ -1,9 +1,9 @@
1
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import { createRequire } from "node:module";
5
6
  import path from "node:path";
6
- import { recipeRuntimeDir } from "../../paths.js";
7
7
  function extensionBrowserDoctorCheck(target) {
8
8
  const requireFromTarget = createRequire(path.join(path.resolve(target), "package.json"));
9
9
  for (const packageName of ["@playwright/test", "playwright"]) {
@@ -1,10 +1,9 @@
1
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { createRequire } from "node:module";
2
3
  import path from "node:path";
3
- import { recipeRuntimeDir, runnerDir } from "../../paths.js";
4
4
  import { resolveExtensionId } from "./extension-id.js";
5
- const requireFromHarness = createRequire(import.meta.url);
6
- const { stampHomeTabsViaCdp } = requireFromHarness(
7
- path.join(runnerDir, "adapters/extension/lib/slot-title.cjs")
5
+ const { stampHomeTabsViaCdp } = createRequire(import.meta.url)(
6
+ "@farmslot/adapter-web/slot-title"
8
7
  );
9
8
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10
9
  async function jsonList(port) {
@@ -200,6 +199,8 @@ async function ensureExtensionReady(target, options) {
200
199
  target: resolved,
201
200
  cdpPort,
202
201
  extensionId,
202
+ homePage: "home.html",
203
+ defaultTitle: "MetaMask",
203
204
  runtimeDir: recipeRuntimeDir()
204
205
  });
205
206
  if (stamp.slotId || stamp.stamped > 0 || stamp.skipped > 0) {
@@ -1,7 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { recipeRuntimeDir } from "../../paths.js";
4
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
5
5
  import { releaseArtifactState, runtimeDistCheck } from "./runtime-decision.js";
6
6
  const DIST_MANIFEST = "dist/chrome/manifest.json";
7
7
  function extensionIdFromKey(keyBase64) {
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
3
4
  import { normalizeFeatureFlagRecord } from "../../feature-flags.js";
4
- import { recipeRuntimeDir } from "../../paths.js";
5
5
  import { releaseArtifactState } from "./runtime-decision.js";
6
6
  function extensionRuntimeManifestPath(target) {
7
7
  return path.join(
@@ -1,8 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { colorHumanMessage } from "../../cli-color.js";
3
+ import { colorHumanMessage, EXIT, spawnScriptStreaming } from "@farmslot/recipe-cli/harness";
4
4
  import { FIXTURE_STREAM_TIMEOUT_MS } from "../../commands/fixtures.js";
5
- import { EXIT, spawnScriptStreaming } from "../../commands/shared.js";
6
5
  import { sameFeatureFlags } from "../../feature-flags.js";
7
6
  import { runnerDir } from "../../paths.js";
8
7
  import { readExtensionPinnedFlags } from "./feature-flags.js";
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { colorHumanMessage } from "../../cli-color.js";
2
+ import { colorHumanMessage, EXIT, spawnScript } from "@farmslot/recipe-cli/harness";
3
3
  import {
4
4
  extensionDepsBlock,
5
5
  extensionRuntimeReusable,
@@ -7,18 +7,17 @@ import {
7
7
  launchExtension
8
8
  } from "../../commands/launch/extension.js";
9
9
  import { shellQuote } from "../../commands/launch/quote.js";
10
- import { EXIT, spawnScript } from "../../commands/shared.js";
11
10
  import {
12
11
  fixtureFeatureFlags,
13
12
  sameFeatureFlags,
14
13
  toRemoteFlagSpec
15
14
  } from "../../feature-flags.js";
16
- import { RECOVERY_CODE } from "../../heal-bounds.js";
17
15
  import { runnerDir, walletFixturePath } from "../../paths.js";
18
16
  import { ensureExtensionConsoleCapture } from "./console-capture.js";
19
17
  import { readExtensionPinnedFlags } from "./feature-flags.js";
20
18
  import { extensionProductConfigBlock } from "./product-config.js";
21
19
  import { releaseArtifactState, runtimeDistCheck } from "./runtime-decision.js";
20
+ const CHROME_REOPENED = "chrome.reopened";
22
21
  const DEFAULT_EXTENSION_DAPP_URL = "https://metamask.github.io/test-dapp/";
23
22
  function verifyExtensionPin(target, requested) {
24
23
  const pinned = readExtensionPinnedFlags(target);
@@ -223,7 +222,7 @@ const extensionLaunchProfile = {
223
222
  originalError: rebuildAttempt.output.trim() || void 0
224
223
  });
225
224
  },
226
- recovery: () => ({ code: RECOVERY_CODE.extension, restartApp: false }),
225
+ recovery: () => ({ code: CHROME_REOPENED, restartApp: false }),
227
226
  // --watch only restarts the webpack watcher; the snapshot, and its pin, are
228
227
  // untouched, so the live record is reported rather than verified against a
229
228
  // request the watcher could never apply (--remote-flag is refused with it;
@@ -307,7 +306,7 @@ const extensionLaunchProfile = {
307
306
  return void 0;
308
307
  },
309
308
  verifyArgs: () => process.env.CDP_PORT ? ["--cdp-port", process.env.CDP_PORT] : [],
310
- platformLabel: ({ mobileTarget, displayMode }) => mobileTarget ?? displayMode,
309
+ platformLabel: ({ platformTarget, displayMode }) => platformTarget ?? displayMode,
311
310
  deviceLabel: ({ displayMode }) => displayMode
312
311
  };
313
312
  export {
@@ -1,8 +1,7 @@
1
1
  import fs from "node:fs";
2
+ import { recipeRuntimeDir, isRecord } from "@farmslot/recipe-cli/harness";
2
3
  import { createRequire } from "node:module";
3
4
  import path from "node:path";
4
- import { isRecord } from "../../commands/parse-args.js";
5
- import { recipeRuntimeDir } from "../../paths.js";
6
5
  function readRecord(file) {
7
6
  try {
8
7
  const recorded = JSON.parse(fs.readFileSync(file, "utf8"));
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import brokerModule from "../../../adapters/mobile/bridge-runtime/lib/cdp-broker.cjs";
2
+ import brokerModule from "@farmslot/recipe-runner/cdp-broker";
3
3
  import {
4
4
  asBrowserCdpTarget,
5
5
  createBrowserCdpClient,
@@ -204,6 +204,19 @@ async function waitForSocket(socketPath) {
204
204
  }
205
205
  throw new Error("Extension network observer broker did not start.");
206
206
  }
207
+ async function createExtensionNetworkBackend(env, artifactsDir) {
208
+ return createExtensionNetworkObserver(extensionCdpPort(env), artifactsDir);
209
+ }
210
+ function extensionCdpPort(env) {
211
+ const port = Number(
212
+ env.CDP_PORT ?? env.RECIPE_CDP_PORT ?? process.env.CDP_PORT
213
+ );
214
+ if (!Number.isInteger(port) || port <= 0) {
215
+ throw new Error("Extension network observation requires CDP_PORT.");
216
+ }
217
+ return port;
218
+ }
207
219
  export {
220
+ createExtensionNetworkBackend,
208
221
  createExtensionNetworkObserver
209
222
  };
@@ -1,5 +1,5 @@
1
+ import { readRuntimeContextField, resolveRuntimeContextPath } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
- import { readRuntimeContextField, resolveRuntimeContextPath } from "../../harness.js";
3
3
  import { resolveExtensionSlotPorts } from "../slot-ports.js";
4
4
  function hasArg(args, needle) {
5
5
  return args.some((arg) => arg === needle || arg.startsWith(`${needle}=`));
@@ -1,8 +1,8 @@
1
+ import { shellQuote } from "@farmslot/recipe-cli/harness";
1
2
  import { createHash } from "node:crypto";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import productConfig from "../../../adapters/extension/lib/product-config.cjs";
5
- import { shellQuote } from "../../commands/parse-args.js";
6
6
  const EXTENSION_PRODUCT_CONFIG_FINGERPRINT_FILENAME = "extension-product-config.sha256";
7
7
  function extensionProductConfigFingerprint(target, environment = process.env) {
8
8
  const resolution = productConfig.resolveExtensionInfuraProjectId(target, environment);
@@ -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 { ensureExtensionPerpsBuildFlag, extensionBrowserDoctorCheck, extensionBrowserResolutionDoctorCheck } from "./doctor-checks.js";
3
3
  import { readExtensionPinnedFlags } from "./feature-flags.js";
4
4
  import { extensionProductConfigBlock } from "./product-config.js";
@@ -1,5 +1,5 @@
1
+ import { EXIT, spawnScript, usageOut } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
- import { EXIT, spawnScript, usageOut } from "../../commands/shared.js";
3
3
  import { runnerDir } from "../../paths.js";
4
4
  function reloadExtension(target, json) {
5
5
  const adapter = "extension";
@@ -1,15 +1,24 @@
1
- import { optionString } from "../../commands/parse-args.js";
2
- import { resolveExtensionBrowserPid } from "../../recording-target.js";
1
+ import path from "node:path";
2
+ import { EXIT, optionFlag, optionString } from "@farmslot/recipe-cli/harness";
3
+ import { verifyOwnedCollectorControl } from "../console-capture.js";
4
+ import { resolveExtensionBrowserPid } from "./browser-pid.js";
3
5
  import {
4
6
  ensureExtensionConsoleCapture,
5
7
  extensionCaptureFiles,
6
8
  extensionCdpPort,
7
9
  verifyExtensionConsoleControl
8
10
  } from "./console-capture.js";
11
+ import { emitProofFailure } from "../proof-failure.js";
9
12
  import { launchedBrowser } from "./launched-browser.js";
13
+ const AUTOLAUNCH_ENV = "METAMASK_RECIPE_EXTENSION_AUTOLAUNCH";
10
14
  const extensionRun = {
15
+ platformOptions: (options) => ({ launchExistingDist: optionFlag(options, "launchExistingDist") }),
16
+ activateEnv(_projectRoot, options) {
17
+ if (options.platform?.launchExistingDist) process.env[AUTOLAUNCH_ENV] = "1";
18
+ },
19
+ envKeys: [AUTOLAUNCH_ENV],
11
20
  async prepareRuntime(projectRoot, runtimeOptions) {
12
- if (runtimeOptions.skipExtensionRuntimePrepare === true) return;
21
+ if (runtimeOptions.platform?.skipExtensionRuntimePrepare === true) return;
13
22
  const { releaseArtifactState, runtimeDistCheck } = await import("./runtime-decision.js");
14
23
  const artifact = releaseArtifactState(projectRoot);
15
24
  const runtimeSnapshot = runtimeDistCheck(projectRoot, artifact);
@@ -24,7 +33,7 @@ const extensionRun = {
24
33
  projectRoot,
25
34
  cdpPort: runtimeOptions.cdpPort,
26
35
  slot: runtimeOptions.slot,
27
- launchExistingDist: runtimeOptions.launchExistingDist === true,
36
+ launchExistingDist: runtimeOptions.platform?.launchExistingDist === true,
28
37
  validationRuntimeDir: runtimeOptions.validationRuntimeDir,
29
38
  pageMode: "home-or-sidepanel"
30
39
  });
@@ -33,19 +42,17 @@ const extensionRun = {
33
42
  // the slot's for the check, and the slot's come back afterwards.
34
43
  runtimeCheck({ target, options, heal, state, json, onRecovery }) {
35
44
  return async () => {
36
- const cliOptions = options;
37
45
  const previousCdpPort = process.env.CDP_PORT;
38
46
  const previousRecipeCdpPort = process.env.RECIPE_CDP_PORT;
39
47
  const previousWatcherPort = process.env.WATCHER_PORT;
40
- const explicitCdpPort = optionString(cliOptions, "cdpPort");
41
- const explicitWatcherPort = optionString(cliOptions, "watcherPort") ?? optionString(cliOptions, "metroPort");
48
+ const explicitCdpPort = optionString(options, "cdpPort");
49
+ const explicitWatcherPort = optionString(options, "watcherPort") ?? optionString(options, "metroPort");
42
50
  if (explicitCdpPort) {
43
51
  process.env.CDP_PORT = explicitCdpPort;
44
52
  process.env.RECIPE_CDP_PORT = explicitCdpPort;
45
53
  }
46
54
  if (explicitWatcherPort) process.env.WATCHER_PORT = explicitWatcherPort;
47
55
  try {
48
- const { ensureExtensionProofRuntime } = await import("../../commands/run-engine.js");
49
56
  return await ensureExtensionProofRuntime(target, heal, state, json, { onRecovery });
50
57
  } finally {
51
58
  restoreEnv("CDP_PORT", previousCdpPort);
@@ -54,12 +61,120 @@ const extensionRun = {
54
61
  }
55
62
  };
56
63
  },
57
- launchedBrowser: (target, artifactsDir, cdpPort) => launchedBrowser(target, artifactsDir, cdpPort)
64
+ launchedBrowser: (target, artifactsDir, cdpPort) => launchedBrowser(target, artifactsDir, cdpPort),
65
+ browserProvenance: (browser) => ({
66
+ browser: browser.browser,
67
+ browser_version: browser.version,
68
+ browser_source: browser.source,
69
+ extension_loading: browser.extensionLoading
70
+ })
58
71
  };
72
+ async function ensureExtensionProofRuntime(target, heal, state, json, deps = {}) {
73
+ const decide = deps.decide ?? (async () => {
74
+ const { decideExtensionReadiness } = await import("./runtime-decision.js");
75
+ const port = Number(process.env.CDP_PORT);
76
+ return decideExtensionReadiness(target, {
77
+ ...Number.isInteger(port) && port > 0 ? { cdpPort: port } : {},
78
+ pageMode: "home-or-sidepanel"
79
+ });
80
+ });
81
+ const initial = await decide();
82
+ if (initial.decision === "ready") return null;
83
+ if (initial.reasonCode === "release-artifact-identity-invalid" || initial.reasonCode === "release-artifact-runtime-missing" || initial.reasonCode === "release-artifact-runtime-stale") {
84
+ const message = initial.reasons[0] ?? "The loaded release artifact identity is invalid.";
85
+ const userAction2 = "rerun runtime-launch with the same artifact source";
86
+ emitProofFailure(json, {
87
+ recoverable: false,
88
+ error: { code: "RELEASE_ARTIFACT_NOT_CURRENT", message, userAction: userAction2 }
89
+ });
90
+ return EXIT.infra;
91
+ }
92
+ if (initial.reasonCode === "runtime-dist-check-failed") {
93
+ const message = `Extension proof runtime freshness could not be checked: ${initial.reasons[0] ?? initial.decision}`;
94
+ const userAction2 = `mm-harness doctor --adapter extension --target ${JSON.stringify(path.resolve(target))}`;
95
+ emitProofFailure(json, {
96
+ recoverable: false,
97
+ error: { code: "EXTENSION_RUNTIME_CHECK_FAILED", message, userAction: userAction2 }
98
+ });
99
+ return EXIT.infra;
100
+ }
101
+ if (![
102
+ "dist-stale",
103
+ "dist-missing",
104
+ "runtime-dist-stale",
105
+ "runtime-dist-missing"
106
+ ].includes(initial.reasonCode)) {
107
+ return null;
108
+ }
109
+ const tier = initial.decision === "build" ? "build" : "quick";
110
+ const canHeal = heal === "auto" || heal === "infra-only" && tier === "quick";
111
+ const targetArg = JSON.stringify(path.resolve(target));
112
+ const userAction = tier === "build" ? `mm-harness launch --build --verify --adapter extension --target ${targetArg}` : `mm-harness launch --verify --adapter extension --target ${targetArg}`;
113
+ if (!canHeal) {
114
+ const message = `Extension proof runtime is not current (${initial.reasonCode}): ${initial.reasons[0] ?? initial.decision}`;
115
+ emitProofFailure(json, {
116
+ recoverable: true,
117
+ mutations: state.mutations,
118
+ error: { code: "EXTENSION_RUNTIME_NOT_CURRENT", message, userAction }
119
+ });
120
+ return EXIT.infra;
121
+ }
122
+ const recoveryCode = tier === "build" ? "extension.runtime_rebuilt" : "extension.runtime_reloaded";
123
+ const productConfigBlock = tier === "build" ? (await import("./product-config.js")).extensionProductConfigBlock(target) : null;
124
+ if (productConfigBlock) {
125
+ const message = productConfigBlock.message;
126
+ const userAction2 = productConfigBlock.userAction;
127
+ emitProofFailure(json, {
128
+ recoverable: false,
129
+ mutations: state.mutations,
130
+ error: { code: "EXTENSION_PRODUCT_CONFIG_REQUIRED", message, userAction: userAction2 }
131
+ });
132
+ return EXIT.infra;
133
+ }
134
+ state.attemptedRecoveries.push(recoveryCode);
135
+ deps.onRecovery?.(recoveryCode);
136
+ if (!json)
137
+ console.error(
138
+ `\u2192 Extension proof preflight: ${initial.reasonCode}; ${tier === "build" ? "rebuilding" : "reloading"} before execution`
139
+ );
140
+ const launch = deps.launch ?? (async (requestedTier) => {
141
+ const { launchExtension } = await import("../../commands/launch/extension.js");
142
+ const { readExtensionPinnedFlags } = await import("./feature-flags.js");
143
+ const { fixtureFeatureFlags, remoteFlagRequestFor } = await import("../../feature-flags.js");
144
+ const { walletFixturePath } = await import("../../paths.js");
145
+ return launchExtension(
146
+ target,
147
+ requestedTier,
148
+ false,
149
+ "fullscreen",
150
+ remoteFlagRequestFor(readExtensionPinnedFlags(target).pinned ?? fixtureFeatureFlags(walletFixturePath(target)))
151
+ );
152
+ });
153
+ const result = await launch(tier);
154
+ const verified = result.status === 0 ? await decide() : initial;
155
+ if (result.status !== 0 || verified.decision !== "ready") {
156
+ const message = `Extension runtime recovery did not produce a current healthy runtime (${verified.reasonCode}).`;
157
+ emitProofFailure(json, {
158
+ recoverable: false,
159
+ attemptedRecoveries: state.attemptedRecoveries,
160
+ error: { code: "EXTENSION_RUNTIME_RECOVERY_FAILED", message, userAction, originalError: result.output }
161
+ });
162
+ return EXIT.infra;
163
+ }
164
+ state.recovered.push(recoveryCode);
165
+ state.mutations.push({
166
+ type: "runtime",
167
+ action: tier === "build" ? "rebuilt" : "reloaded",
168
+ reasonCode: initial.reasonCode
169
+ });
170
+ return null;
171
+ }
59
172
  const extensionRecording = {
60
173
  target: (context) => resolveExtensionRecordingTarget(context),
61
174
  framed: {
62
- browserPid: (projectRoot, artifactsDir, cdpPort) => resolveExtensionBrowserPid(projectRoot, artifactsDir, cdpPort)
175
+ browserPid: (projectRoot, artifactsDir, cdpPort) => resolveExtensionBrowserPid(projectRoot, artifactsDir, cdpPort),
176
+ // cdp.mjs snapshots the framed recording of this pid.
177
+ activePidEnv: "METAMASK_RECIPE_EXTENSION_ACTIVE_RECORDING_PID"
63
178
  }
64
179
  };
65
180
  async function resolveExtensionRecordingTarget(context) {
@@ -80,17 +195,22 @@ const extensionDiagnostics = {
80
195
  start: (projectRoot) => ensureExtensionConsoleCapture(projectRoot),
81
196
  files: (projectRoot) => extensionCaptureFiles(projectRoot),
82
197
  cdpPort: () => extensionCdpPort(),
83
- verifyControl: (capture) => verifyExtensionConsoleControl(capture)
198
+ verifyControl: (capture) => verifyOwnedCollectorControl(capture, () => verifyExtensionConsoleControl(capture))
84
199
  }
85
200
  };
86
201
  const extensionObservation = {
87
202
  network: {
88
- backend: async (_target, env, artifactsDir) => (await import("../../network-observation.js")).createExtensionNetworkBackend(env, artifactsDir),
203
+ backend: async (_target, env, artifactsDir) => (await import("./network-observer.js")).createExtensionNetworkBackend(env, artifactsDir),
89
204
  actions: true
90
205
  },
91
206
  performance: {
92
- backend: async (target, env, ports) => (await import("../../performance-observation.js")).createExtensionPerformanceObserver(target, env, ports),
93
- platform: () => "extension"
207
+ start: async (context) => {
208
+ const observation = await import("../../performance-observation.js");
209
+ return observation.startRunPerformanceObservation({
210
+ backend: (target, env, ports) => observation.createExtensionPerformanceObserver(target, env, ports),
211
+ platform: () => "extension"
212
+ }, context);
213
+ }
94
214
  }
95
215
  };
96
216
  function restoreEnv(key, value) {
@@ -98,6 +218,7 @@ function restoreEnv(key, value) {
98
218
  else process.env[key] = value;
99
219
  }
100
220
  export {
221
+ ensureExtensionProofRuntime,
101
222
  extensionDiagnostics,
102
223
  extensionObservation,
103
224
  extensionRecording,
@@ -1,3 +1,4 @@
1
+ import { recipeHarnessPath, 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";
@@ -7,7 +8,7 @@ import {
7
8
  depsCheck,
8
9
  INSTALL_MARKERS
9
10
  } from "@farmslot/recipe-runner/runtime/deps-readiness";
10
- import { recipeHarnessPath, recipeRuntimeDir, recipeWatchLogCandidates } from "../../paths.js";
11
+ import { recipeWatchLogCandidates } from "../../paths.js";
11
12
  const WEBPACK_DIRECT_INPUTS = ["package.json", "yarn.lock", ".yarnrc.yml", ".tool-versions"];
12
13
  const WEBPACK_RECURSIVE_INPUTS = ["development/webpack"];
13
14
  const WEBPACK_CACHE_DIR = "node_modules/.cache/webpack";
@@ -1,8 +1,9 @@
1
+ import { recipeHarnessPath, recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { spawn } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import { createRequire } from "node:module";
4
5
  import path from "node:path";
5
- import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, recipeHarnessPath, recipeRuntimeDir, resolveLocalProtocolRoot, runnerDir } from "../../paths.js";
6
+ import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, resolveFarmslotPoolDir, runnerDir } from "../../paths.js";
6
7
  import { poolFilesForHost } from "../resolve-slot-ports.js";
7
8
  import { profileProcessPids } from "./validation-process-ownership.js";
8
9
  const require2 = createRequire(import.meta.url);
@@ -537,12 +538,15 @@ function resolveCdpPort(rawPort, slot) {
537
538
  return port;
538
539
  }
539
540
  function resolveExtensionSlot(projectRoot, requestedSlot) {
540
- const protocolRoot = resolveLocalProtocolRoot();
541
- if (!protocolRoot) {
542
- if (requestedSlot) throw new Error("Extension slot lookup requires a local host checkout when --slot is provided.");
541
+ const poolsDir = resolveFarmslotPoolDir();
542
+ if (!poolsDir) {
543
+ if (requestedSlot) {
544
+ throw new Error(
545
+ "Extension slot lookup needs the farm's pool/*.json when --slot is provided. Run it from the Farmslot checkout that holds pool/ or from a directory beside that checkout."
546
+ );
547
+ }
543
548
  return null;
544
549
  }
545
- const poolsDir = path.join(protocolRoot, "pool");
546
550
  if (!fs.existsSync(poolsDir)) return null;
547
551
  const pools = poolFilesForHost(poolsDir);
548
552
  for (const { data } of [...pools.local, ...pools.other]) {
@@ -1,8 +1,8 @@
1
+ import { recipeRuntimePath, shellQuote } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import path from "node:path";
3
4
  import { defineAdapter } from "@farmslot/adapter-sdk";
4
- import { recipeRuntimePath, runnerDir } from "../../paths.js";
5
- import { shellQuote } from "../../commands/parse-args.js";
5
+ import { runnerDir } from "../../paths.js";
6
6
  import {
7
7
  isExtensionWatcherLive,
8
8
  resolveExtensionSlotPorts,
@@ -15,6 +15,7 @@ import { extensionActions } from "./actions.js";
15
15
  import { extensionHarness, extensionPrepare, extensionRuntimeContext } from "./overlay.js";
16
16
  import { extensionReadiness, extensionRuntimeDecision } from "./readiness.js";
17
17
  import { extensionDiagnostics, extensionObservation, extensionRecording, extensionRun } from "./run.js";
18
+ import { checkoutHas, checkoutHasDir } from "../checkout-files.js";
18
19
  function watcherStatus(buildLog) {
19
20
  if (buildLog === "ok") return "up";
20
21
  if (buildLog === "no-watch") return "down";
@@ -102,6 +103,18 @@ const extensionSurface = defineAdapter({
102
103
  appLogSource(target) {
103
104
  return { label: "extension", path: recipeRuntimePath(target, "extension-console.log") };
104
105
  },
106
+ detect: {
107
+ remote: (url) => url.includes("metamask-extension"),
108
+ files: (target) => checkoutHas(target, "development/skills-sync.ts") || checkoutHasDir(target, "ui") && checkoutHasDir(target, "app/scripts")
109
+ },
110
+ flags: {
111
+ launch: ["build", "watch", "verify", "sidepanel", "fullscreen"],
112
+ commands: ["launchExistingDist", "startWatch", "buildLavamoat"]
113
+ },
114
+ failurePatterns: {
115
+ walletState: /fixture|keyring|not seeded|\bsrp\b|password|onboard/iu,
116
+ transport: /cdp|chrome|port\b|econnrefused|not reachable|watcher|websocket/iu
117
+ },
105
118
  hints: {
106
119
  launch: "mm-harness launch",
107
120
  relaunch: "mm-harness launch --build",
@@ -1,10 +1,10 @@
1
+ import { recipeHarnessPath, recipeHarnessRoot, recipeRuntimeDir, readRuntimeContextField, resolveRuntimeContextPath } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync, spawnSync } from "node:child_process";
2
3
  import crypto from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import http from "node:http";
5
6
  import path from "node:path";
6
- import { readRuntimeContextField, resolveRuntimeContextPath } from "../../harness.js";
7
- import { recipeHarnessPath, recipeHarnessRoot, recipeRuntimeDir, runnerDir } from "../../paths.js";
7
+ import { runnerDir } from "../../paths.js";
8
8
  import { resolveExtensionSlotPorts } from "../slot-ports.js";
9
9
  const USAGE = "Usage: mm-harness verify --adapter extension -- [--target <metamask-extension>] [--out <recipes-dir>] [--cdp-port <port>] [--static-only] [--artifacts-dir <dir>]";
10
10
  const EXTENSION_ID_PATTERN = /^[a-p]{32}$/u;
@@ -1,8 +1,9 @@
1
+ import { recipeHarnessPath } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import { pathToFileURL } from "node:url";
5
- import { recipeHarnessPath, runnerDir } from "../paths.js";
6
+ import { runnerDir } from "../paths.js";
6
7
  import { getAdapterSurface } from "./surface.js";
7
8
  async function ensureHarnessFresh(target, adapter) {
8
9
  if (!fs.existsSync(path.join(target, "package.json")) || !hasCheckoutHead(target)) return;
@@ -1,4 +1,5 @@
1
1
  import { manifestPath } from "../../paths.js";
2
+ import { perpsOrderInputFindings } from "../perps-order-inputs.js";
2
3
  const NATIVE_PROVIDER_UI_ACTIONS = /* @__PURE__ */ new Set([
3
4
  "ui.press",
4
5
  "ui.key_press",
@@ -47,7 +48,9 @@ const mobileActions = {
47
48
  }
48
49
  }),
49
50
  wrap: async (adapters) => (await import("./runtime-identity.js")).withMobileNativeRuntimeIdentity(adapters),
50
- lifecycle: async (adapters) => (await import("./lifecycle.js")).mobileSourceAwareLifecycleAdapters(adapters)
51
+ lifecycle: async (adapters) => (await import("./lifecycle.js")).mobileSourceAwareLifecycleAdapters(adapters),
52
+ // ensure_positions and ensure_orders with an open or present state place an order.
53
+ inputFindings: (nodeId, node) => perpsOrderInputFindings(nodeId, node, { ensureChecksOnly: false })
51
54
  };
52
55
  export {
53
56
  mobileActions
@@ -1,8 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import {
4
- summarizeFrames
5
- } from "./frame-metrics.js";
3
+ import { summarizeFrames } from "@farmslot/adapter-rn";
6
4
  const execFileAsync = promisify(execFile);
7
5
  const POLL_INTERVAL_MS = 750;
8
6
  const MAX_FRAMES = 1e5;
@@ -0,0 +1,10 @@
1
+ function mobileAppIdentityEnv(env = {}) {
2
+ return {
3
+ ANDROID_PACKAGE_ID: env.ANDROID_PACKAGE_ID ?? process.env.ANDROID_PACKAGE_ID ?? "io.metamask",
4
+ IOS_BUNDLE_ID: env.IOS_BUNDLE_ID ?? process.env.IOS_BUNDLE_ID ?? "io.metamask.MetaMask",
5
+ METAMASK_RECIPE_MOBILE_APP_LABEL: env.METAMASK_RECIPE_MOBILE_APP_LABEL ?? process.env.METAMASK_RECIPE_MOBILE_APP_LABEL ?? "MetaMask"
6
+ };
7
+ }
8
+ export {
9
+ mobileAppIdentityEnv
10
+ };
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import {
4
4
  mobileToolRecovery,
5
5
  resolveMobileToolPath
6
- } from "../../../library/actions/mobile/platform/tool-paths.mjs";
6
+ } from "@farmslot/adapter-rn";
7
7
  import { runnerDir } from "../../paths.js";
8
8
  function mobileToolDoctorChecks(platform) {
9
9
  const specs = platform === "android" ? [{ tool: "adb", platform: "Android" }] : platform === "ios" ? [{ tool: "idb", platform: "iOS" }] : [