@deeeed/metamask-harness 0.78.0 → 0.80.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 (215) hide show
  1. package/CHANGELOG.md +55 -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 +2 -34
  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/ensure-runner-deps.sh +2 -2
  21. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  22. package/adapters/shared/open-debug.mjs +2 -2
  23. package/adapters/shared/runner-deps-ready.mjs +1 -1
  24. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  25. package/adapters/terminal/cleanup.mjs +2 -1
  26. package/adapters/terminal/inject.mjs +2 -1
  27. package/adapters/terminal/lib/cdp-client.mjs +1 -1
  28. package/bin/mm-harness +9 -28
  29. package/dist/adapters/checkout-files.js +16 -0
  30. package/dist/adapters/console-capture.js +14 -1
  31. package/dist/adapters/core/actions.js +4 -3
  32. package/dist/adapters/core/overlay.js +1 -1
  33. package/dist/adapters/core/readiness.js +28 -1
  34. package/dist/adapters/core/run.js +3 -3
  35. package/dist/adapters/core/surface.js +22 -5
  36. package/dist/adapters/extension/browser-pid.js +56 -0
  37. package/dist/adapters/extension/console-capture.js +1 -1
  38. package/dist/adapters/extension/doctor-checks.js +1 -1
  39. package/dist/adapters/extension/ensure-ready.js +5 -4
  40. package/dist/adapters/extension/extension-id.js +1 -1
  41. package/dist/adapters/extension/feature-flags.js +1 -1
  42. package/dist/adapters/extension/fixtures.js +1 -2
  43. package/dist/adapters/extension/launch.js +4 -5
  44. package/dist/adapters/extension/launched-browser.js +1 -2
  45. package/dist/adapters/extension/network-observer.js +1 -1
  46. package/dist/adapters/extension/overlay.js +1 -1
  47. package/dist/adapters/extension/product-config.js +1 -1
  48. package/dist/adapters/extension/readiness.js +1 -1
  49. package/dist/adapters/extension/reload.js +1 -1
  50. package/dist/adapters/extension/run.js +24 -10
  51. package/dist/adapters/extension/runtime-decision.js +3 -2
  52. package/dist/adapters/extension/runtime.js +9 -5
  53. package/dist/adapters/extension/surface.js +20 -5
  54. package/dist/adapters/extension/verify.js +2 -2
  55. package/dist/adapters/harness-freshness.js +2 -1
  56. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  57. package/dist/adapters/mobile/app-identity.js +10 -0
  58. package/dist/adapters/mobile/doctor-checks.js +1 -1
  59. package/dist/adapters/mobile/fixtures.js +1 -1
  60. package/dist/adapters/mobile/launch.js +27 -28
  61. package/dist/adapters/mobile/lifecycle.js +1 -1
  62. package/dist/adapters/mobile/metro-env.js +9 -59
  63. package/dist/adapters/mobile/performance-observer.js +1 -1
  64. package/dist/adapters/mobile/prepare-target.js +1 -1
  65. package/dist/adapters/mobile/prepare.js +4 -5
  66. package/dist/adapters/mobile/provision.js +1 -1
  67. package/dist/adapters/mobile/readiness.js +1 -1
  68. package/dist/adapters/mobile/recording-target.js +15 -0
  69. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  70. package/dist/adapters/mobile/reload.js +1 -1
  71. package/dist/adapters/mobile/run.js +9 -8
  72. package/dist/adapters/mobile/runtime-decision.js +5 -7
  73. package/dist/adapters/mobile/source-freshness.js +22 -126
  74. package/dist/adapters/mobile/surface.js +31 -6
  75. package/dist/adapters/mobile/ui.js +16 -5
  76. package/dist/adapters/mobile/verify.js +2 -3
  77. package/dist/adapters/performance/cdp-trace.js +1 -1
  78. package/dist/adapters/performance/device-platform.js +1 -1
  79. package/dist/adapters/registry.js +2 -14
  80. package/dist/adapters/slot-ports.js +1 -1
  81. package/dist/adapters/surface.js +1 -0
  82. package/dist/adapters/terminal/console-capture.js +1 -1
  83. package/dist/adapters/terminal/launch.js +1 -1
  84. package/dist/adapters/terminal/run.js +25 -2
  85. package/dist/adapters/terminal/surface.js +12 -4
  86. package/dist/adapters.js +8 -4
  87. package/dist/advisor-policy.js +1 -1
  88. package/dist/checkout-view.js +1 -2
  89. package/dist/cli.js +28 -15
  90. package/dist/command-contract.js +1 -26
  91. package/dist/commands/call.js +10 -24
  92. package/dist/commands/check.js +3 -4
  93. package/dist/commands/checklist.js +1 -1
  94. package/dist/commands/completion-candidates.js +2 -10
  95. package/dist/commands/config.js +2 -2
  96. package/dist/commands/core-readiness.js +12 -7
  97. package/dist/commands/debug.js +3 -3
  98. package/dist/commands/device-target.js +5 -5
  99. package/dist/commands/doctor.js +2 -14
  100. package/dist/commands/domain.js +2 -2
  101. package/dist/commands/ensure-ready.js +1 -7
  102. package/dist/commands/fixtures.js +5 -6
  103. package/dist/commands/help.js +3 -4
  104. package/dist/commands/launch/device.js +10 -17
  105. package/dist/commands/launch/extension.js +3 -9
  106. package/dist/commands/launch/mobile.js +1 -1
  107. package/dist/commands/list-executables.js +1 -3
  108. package/dist/commands/logs.js +3 -3
  109. package/dist/commands/manifest.js +1 -12
  110. package/dist/commands/mobile-device-view.js +4 -2
  111. package/dist/commands/overlay.js +110 -0
  112. package/dist/commands/pin.js +1 -1
  113. package/dist/commands/pr-body.js +2 -2
  114. package/dist/commands/prepare.js +6 -11
  115. package/dist/commands/provision.js +3 -12
  116. package/dist/commands/recipe-advice.js +1 -2
  117. package/dist/commands/recipe-quality.js +2 -3
  118. package/dist/commands/resolve-extension.js +1 -7
  119. package/dist/commands/review.js +4 -4
  120. package/dist/commands/run-engine.js +12 -51
  121. package/dist/commands/run.js +4 -25
  122. package/dist/commands/runtime-decision.js +1 -6
  123. package/dist/commands/runtime-health.js +1 -7
  124. package/dist/commands/runtime-launch.js +1 -13
  125. package/dist/commands/self-test.js +2 -6
  126. package/dist/commands/status-watch.js +1 -2
  127. package/dist/commands/status.js +2 -5
  128. package/dist/commands/task-init.js +2 -3
  129. package/dist/commands/tutorial.js +2 -2
  130. package/dist/commands/update.js +1 -1
  131. package/dist/completions-cache.js +1 -1
  132. package/dist/console-classifier.js +19 -0
  133. package/dist/discovery.js +1 -1
  134. package/dist/doctor.js +3 -4
  135. package/dist/funding-execution-context.js +15 -1
  136. package/dist/host.js +32 -0
  137. package/dist/index.js +1 -6
  138. package/dist/live-tsx.js +49 -0
  139. package/dist/mm-harness-cli.js +2 -4
  140. package/dist/network-observation.js +4 -4
  141. package/dist/paths.js +48 -78
  142. package/dist/performance-observation.js +2 -2
  143. package/dist/progress.js +1 -1
  144. package/dist/recipe-assistance-context.js +2 -4
  145. package/dist/recipe-security.js +2 -59
  146. package/dist/review/knowledge.js +3 -3
  147. package/dist/runner.js +5 -3
  148. package/dist/runtime-context.js +1 -1
  149. package/dist/task-view.js +3 -4
  150. package/docs/CONTRIBUTING.md +3 -3
  151. package/docs/cli-contract.md +2 -2
  152. package/docs/feature-guard.md +90 -0
  153. package/library/README.md +3 -1
  154. package/library/actions/core/perps/_controller.mjs +153 -130
  155. package/library/actions/core/perps/quote_fees.mjs +58 -0
  156. package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
  157. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  158. package/library/actions/extension/feature_flags/state.mjs +1 -1
  159. package/library/actions/extension/platform/cdp.mjs +2 -8
  160. package/library/actions/harness-exports.mjs +1 -6
  161. package/library/actions/mobile/app/network-control.mjs +6 -8
  162. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  163. package/library/actions/mobile/platform/bridge.mjs +8 -8
  164. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  165. package/library/actions/mobile/platform/native-session.mjs +3 -4
  166. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  167. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  168. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  169. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  170. package/library/actions/shared/console-findings.mjs +1 -1
  171. package/library/actions/terminal/platform/runtime.mjs +2 -1
  172. package/library/manifests/core.action-manifest.json +83 -13
  173. package/package.json +8 -8
  174. package/adapters/extension/lib/slot-title.cjs +0 -292
  175. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  176. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  177. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  178. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  179. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  180. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  181. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  182. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  183. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  184. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  185. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  186. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  187. package/adapters/mobile/launch-metro.cjs +0 -77
  188. package/adapters/mobile/metro-config.cjs +0 -98
  189. package/adapters/mobile/metro-log-generation.cjs +0 -106
  190. package/dist/adapters/mobile/frame-metrics.js +0 -118
  191. package/dist/adapters/mobile/video-recorder.js +0 -289
  192. package/dist/artifact-files.js +0 -92
  193. package/dist/checkout-lock.js +0 -149
  194. package/dist/cli-color.js +0 -98
  195. package/dist/command-journal.js +0 -308
  196. package/dist/commands/last.js +0 -60
  197. package/dist/commands/launch/index.js +0 -109
  198. package/dist/commands/parse-args.js +0 -279
  199. package/dist/commands/reload.js +0 -41
  200. package/dist/commands/run-report.js +0 -241
  201. package/dist/commands/shared.js +0 -323
  202. package/dist/commands/stop.js +0 -60
  203. package/dist/devices.js +0 -100
  204. package/dist/execution-provenance.js +0 -332
  205. package/dist/harness.js +0 -451
  206. package/dist/heal-bounds.js +0 -137
  207. package/dist/json-stream.js +0 -60
  208. package/dist/leaf-invoke.js +0 -19
  209. package/dist/library-provenance.js +0 -33
  210. package/dist/live-adapter-contract.js +0 -380
  211. package/dist/recording-target.js +0 -120
  212. package/dist/run-diagnostics.js +0 -364
  213. package/dist/run-recording.js +0 -481
  214. package/dist/runtime-proof.js +0 -90
  215. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
@@ -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,
@@ -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,5 +1,6 @@
1
- import { optionString } from "../../commands/parse-args.js";
2
- import { resolveExtensionBrowserPid } from "../../recording-target.js";
1
+ import { optionFlag, optionString } from "@farmslot/recipe-cli/harness";
2
+ import { verifyOwnedCollectorControl } from "../console-capture.js";
3
+ import { resolveExtensionBrowserPid } from "./browser-pid.js";
3
4
  import {
4
5
  ensureExtensionConsoleCapture,
5
6
  extensionCaptureFiles,
@@ -7,9 +8,15 @@ import {
7
8
  verifyExtensionConsoleControl
8
9
  } from "./console-capture.js";
9
10
  import { launchedBrowser } from "./launched-browser.js";
11
+ const AUTOLAUNCH_ENV = "METAMASK_RECIPE_EXTENSION_AUTOLAUNCH";
10
12
  const extensionRun = {
13
+ platformOptions: (options) => ({ launchExistingDist: optionFlag(options, "launchExistingDist") }),
14
+ activateEnv(_projectRoot, options) {
15
+ if (options.platform?.launchExistingDist) process.env[AUTOLAUNCH_ENV] = "1";
16
+ },
17
+ envKeys: [AUTOLAUNCH_ENV],
11
18
  async prepareRuntime(projectRoot, runtimeOptions) {
12
- if (runtimeOptions.skipExtensionRuntimePrepare === true) return;
19
+ if (runtimeOptions.platform?.skipExtensionRuntimePrepare === true) return;
13
20
  const { releaseArtifactState, runtimeDistCheck } = await import("./runtime-decision.js");
14
21
  const artifact = releaseArtifactState(projectRoot);
15
22
  const runtimeSnapshot = runtimeDistCheck(projectRoot, artifact);
@@ -24,7 +31,7 @@ const extensionRun = {
24
31
  projectRoot,
25
32
  cdpPort: runtimeOptions.cdpPort,
26
33
  slot: runtimeOptions.slot,
27
- launchExistingDist: runtimeOptions.launchExistingDist === true,
34
+ launchExistingDist: runtimeOptions.platform?.launchExistingDist === true,
28
35
  validationRuntimeDir: runtimeOptions.validationRuntimeDir,
29
36
  pageMode: "home-or-sidepanel"
30
37
  });
@@ -33,12 +40,11 @@ const extensionRun = {
33
40
  // the slot's for the check, and the slot's come back afterwards.
34
41
  runtimeCheck({ target, options, heal, state, json, onRecovery }) {
35
42
  return async () => {
36
- const cliOptions = options;
37
43
  const previousCdpPort = process.env.CDP_PORT;
38
44
  const previousRecipeCdpPort = process.env.RECIPE_CDP_PORT;
39
45
  const previousWatcherPort = process.env.WATCHER_PORT;
40
- const explicitCdpPort = optionString(cliOptions, "cdpPort");
41
- const explicitWatcherPort = optionString(cliOptions, "watcherPort") ?? optionString(cliOptions, "metroPort");
46
+ const explicitCdpPort = optionString(options, "cdpPort");
47
+ const explicitWatcherPort = optionString(options, "watcherPort") ?? optionString(options, "metroPort");
42
48
  if (explicitCdpPort) {
43
49
  process.env.CDP_PORT = explicitCdpPort;
44
50
  process.env.RECIPE_CDP_PORT = explicitCdpPort;
@@ -54,12 +60,20 @@ const extensionRun = {
54
60
  }
55
61
  };
56
62
  },
57
- launchedBrowser: (target, artifactsDir, cdpPort) => launchedBrowser(target, artifactsDir, cdpPort)
63
+ launchedBrowser: (target, artifactsDir, cdpPort) => launchedBrowser(target, artifactsDir, cdpPort),
64
+ browserProvenance: (browser) => ({
65
+ browser: browser.browser,
66
+ browser_version: browser.version,
67
+ browser_source: browser.source,
68
+ extension_loading: browser.extensionLoading
69
+ })
58
70
  };
59
71
  const extensionRecording = {
60
72
  target: (context) => resolveExtensionRecordingTarget(context),
61
73
  framed: {
62
- browserPid: (projectRoot, artifactsDir, cdpPort) => resolveExtensionBrowserPid(projectRoot, artifactsDir, cdpPort)
74
+ browserPid: (projectRoot, artifactsDir, cdpPort) => resolveExtensionBrowserPid(projectRoot, artifactsDir, cdpPort),
75
+ // cdp.mjs snapshots the framed recording of this pid.
76
+ activePidEnv: "METAMASK_RECIPE_EXTENSION_ACTIVE_RECORDING_PID"
63
77
  }
64
78
  };
65
79
  async function resolveExtensionRecordingTarget(context) {
@@ -80,7 +94,7 @@ const extensionDiagnostics = {
80
94
  start: (projectRoot) => ensureExtensionConsoleCapture(projectRoot),
81
95
  files: (projectRoot) => extensionCaptureFiles(projectRoot),
82
96
  cdpPort: () => extensionCdpPort(),
83
- verifyControl: (capture) => verifyExtensionConsoleControl(capture)
97
+ verifyControl: (capture) => verifyOwnedCollectorControl(capture, () => verifyExtensionConsoleControl(capture))
84
98
  }
85
99
  };
86
100
  const extensionObservation = {
@@ -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";
@@ -6,8 +7,8 @@ import { extensionTreeSha256 } from "./artifact-integrity.js";
6
7
  import {
7
8
  depsCheck,
8
9
  INSTALL_MARKERS
9
- } from "@farmslot/recipe-harness/runtime/deps-readiness";
10
- import { recipeHarnessPath, recipeRuntimeDir, recipeWatchLogCandidates } from "../../paths.js";
10
+ } from "@farmslot/recipe-runner/runtime/deps-readiness";
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,7 +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
- import { recipeRuntimePath, runnerDir } from "../../paths.js";
4
- import { shellQuote } from "../../commands/parse-args.js";
4
+ import { defineAdapter } from "@farmslot/adapter-sdk";
5
+ import { runnerDir } from "../../paths.js";
5
6
  import {
6
7
  isExtensionWatcherLive,
7
8
  resolveExtensionSlotPorts,
@@ -14,6 +15,7 @@ import { extensionActions } from "./actions.js";
14
15
  import { extensionHarness, extensionPrepare, extensionRuntimeContext } from "./overlay.js";
15
16
  import { extensionReadiness, extensionRuntimeDecision } from "./readiness.js";
16
17
  import { extensionDiagnostics, extensionObservation, extensionRecording, extensionRun } from "./run.js";
18
+ import { checkoutHas, checkoutHasDir } from "../checkout-files.js";
17
19
  function watcherStatus(buildLog) {
18
20
  if (buildLog === "ok") return "up";
19
21
  if (buildLog === "no-watch") return "down";
@@ -24,8 +26,9 @@ function extensionRuntimeNextAction(reasonCode, target) {
24
26
  if (reasonCode === "rpc-execution-failed") return `mm-harness logs --target ${resolved} --full`;
25
27
  return reasonCode === "deps-missing" || reasonCode === "deps-stale" ? `cd ${resolved} && yarn install --immutable` : `mm-harness launch --adapter extension --target ${resolved}`;
26
28
  }
27
- const extensionSurface = {
28
- adapter: "extension",
29
+ const extensionSurface = defineAdapter({
30
+ id: "extension",
31
+ sdkVersion: 1,
29
32
  headless: false,
30
33
  resolveSlotPorts(target) {
31
34
  resolveExtensionSlotPorts(target);
@@ -100,6 +103,18 @@ const extensionSurface = {
100
103
  appLogSource(target) {
101
104
  return { label: "extension", path: recipeRuntimePath(target, "extension-console.log") };
102
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
+ },
103
118
  hints: {
104
119
  launch: "mm-harness launch",
105
120
  relaunch: "mm-harness launch --build",
@@ -135,7 +150,7 @@ const extensionSurface = {
135
150
  recording: extensionRecording,
136
151
  diagnostics: extensionDiagnostics,
137
152
  observation: extensionObservation
138
- };
153
+ });
139
154
  adapterRegistry.register(extensionSurface);
140
155
  export {
141
156
  extensionRuntimeNextAction,
@@ -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,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" }] : [
@@ -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 {