@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
@@ -4,9 +4,8 @@ import path from "node:path";
4
4
  import { runnerDir, walletFixturePath } from "../paths.js";
5
5
  import { getAdapterSurface } from "../adapters/surface.js";
6
6
  import { fixtureFileSummary, fixtureSummary } from "../doctor.js";
7
- import { acquireCheckoutLock } from "../checkout-lock.js";
7
+ import { acquireCheckoutLock, adapterDetectNext, checkoutBusyOut, EXIT, flag, parseFlags, resolveFlagsAdapter, scriptOverride, spawnScript, spawnScriptStreaming, str, targetOf, usageOut } from "@farmslot/recipe-cli/harness";
8
8
  import { applyDeviceTargeting } from "./device-target.js";
9
- import { ADAPTER_DETECT_NEXT, checkoutBusyOut, EXIT, flag, parseFlags, resolveAdapter, scriptOverride, spawnScript, spawnScriptStreaming, str, targetOf, usageOut } from "./shared.js";
10
9
  import { FeatureFlagSpecError, fixtureFeatureFlags, parseRemoteFlagSpec } from "../feature-flags.js";
11
10
  const FIXTURES_BOOLEANS = /* @__PURE__ */ new Set(["dev", "force", "json"]);
12
11
  const FIXTURE_STREAM_TIMEOUT_MS = 12e4;
@@ -39,9 +38,9 @@ function fixturesStatus(options) {
39
38
  );
40
39
  }
41
40
  const target = targetOf(options);
42
- const adapter = resolveAdapter(options, target);
41
+ const adapter = resolveFlagsAdapter(options, target);
43
42
  if (!adapter) {
44
- return usageOut(json, "fixtures", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
43
+ return usageOut(json, "fixtures", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
45
44
  }
46
45
  const canonical = walletFixturePath(target);
47
46
  const summary = fixtureSummary(target, adapter);
@@ -115,9 +114,9 @@ async function handleFixturesLocked(argv) {
115
114
  );
116
115
  }
117
116
  const target = targetOf(options);
118
- const adapter = resolveAdapter(options, target);
117
+ const adapter = resolveFlagsAdapter(options, target);
119
118
  if (!adapter) {
120
- return usageOut(json, "fixtures", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
119
+ return usageOut(json, "fixtures", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
121
120
  }
122
121
  const surface = getAdapterSurface(adapter);
123
122
  surface.resolveSlotPorts(target);
@@ -1,9 +1,8 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { color } from "../cli-color.js";
4
- import { detectAdapter } from "../harness.js";
5
- import { optionString, parseArgs, targetPath } from "./parse-args.js";
3
+ import { color, detectAdapter, optionString, parseArgs, targetPath } from "@farmslot/recipe-cli/harness";
6
4
  import { handleHelpReview } from "./review.js";
5
+ import "../adapters/surface.js";
7
6
  const UNSCOPED_NEXT = "cd into a MetaMask checkout or pass --adapter <mobile|extension|core|terminal>";
8
7
  function validHelpLine(value) {
9
8
  return typeof value === "string" || Boolean(value && typeof value.text === "string" && Array.isArray(value.adapters) && value.adapters.length > 0 && value.adapters.every((adapter) => ["mobile", "extension", "core", "terminal"].includes(adapter)));
@@ -70,7 +69,7 @@ function renderRecipeHelp(topic, harnessVersion, adapter) {
70
69
  `;
71
70
  }
72
71
  function handleHelp(argv, context) {
73
- const parsed = parseArgs(argv, "help");
72
+ const parsed = parseArgs(argv);
74
73
  if (parsed.positional[0] === "review") return handleHelpReview(argv, context.harnessVersion);
75
74
  const target = targetPath(parsed.options);
76
75
  const adapter = optionString(parsed.options, "adapter") ?? detectAdapter(target) ?? "unscoped";
@@ -1,15 +1,8 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import fs from "node:fs";
3
- import { color, colorHumanMessage } from "../../cli-color.js";
4
- import { handleHarness } from "../../harness.js";
5
- import { recipeRuntimePath, walletFixturePath } from "../../paths.js";
3
+ import { color, colorHumanMessage, recipeRuntimePath, handleHarness, EXIT, flag, str, checkHealBounds, ensureOverlay, newHealState } from "@farmslot/recipe-cli/harness";
4
+ import { walletFixturePath } from "../../paths.js";
6
5
  import { getAdapterSurface } from "../../adapters/surface.js";
7
- import { EXIT, flag, str } from "../shared.js";
8
- import {
9
- checkHealBounds,
10
- ensureOverlay,
11
- newHealState
12
- } from "../../heal-bounds.js";
13
6
  import {
14
7
  FeatureFlagSpecError,
15
8
  fixtureFeatureFlags,
@@ -131,7 +124,7 @@ async function runDeviceLaunch(context, profile) {
131
124
  if (heal === "off") {
132
125
  return launch.fail(state, {
133
126
  code: "LAUNCH_FAILED",
134
- message: `launch ${adapter}${launch.mobileTarget ? ` ${launch.mobileTarget}` : ""} failed (healing off)`,
127
+ message: `launch ${adapter}${launch.platformTarget ? ` ${launch.platformTarget}` : ""} failed (healing off)`,
135
128
  recoverable: false,
136
129
  exitCode: EXIT.infra
137
130
  });
@@ -166,7 +159,7 @@ async function runDeviceLaunch(context, profile) {
166
159
  if (retryReadinessFailure) return launch.fail(state, retryReadinessFailure);
167
160
  return launch.fail(state, {
168
161
  code: "SAME_RECOVERY_TWICE",
169
- message: `launch ${adapter}${launch.mobileTarget ? ` ${launch.mobileTarget}` : ""} failed again after ${recovery.code} recovery \u2014 refusing to loop.`,
162
+ message: `launch ${adapter}${launch.platformTarget ? ` ${launch.platformTarget}` : ""} failed again after ${recovery.code} recovery \u2014 refusing to loop.`,
170
163
  recoverable: false,
171
164
  exitCode: EXIT.bounded,
172
165
  originalError: attempt.output.trim() || void 0
@@ -231,7 +224,7 @@ async function settleLaunch(launch, profile, state, sidepanelRecoveryAllowed, pr
231
224
  return launchPass(launch, profile, state, proven.flags);
232
225
  }
233
226
  function launchPass(launch, profile, state, remoteFlags) {
234
- const { adapter, mobileTarget, target, stream, tier } = launch;
227
+ const { adapter, platformTarget, target, stream, tier } = launch;
235
228
  if (stream.enabled) {
236
229
  for (const mutation of state.mutations) stream.mutation(mutation);
237
230
  for (const recovery of state.recovered) stream.recovery(recovery);
@@ -269,13 +262,13 @@ function launchPass(launch, profile, state, remoteFlags) {
269
262
  const tierNote = tier === "quick" ? "quick relaunch, no native build" : tier;
270
263
  const devNote = process.env.MM_HARNESS_BIN ? ` ${color("dim", "[dev: MM_HARNESS_BIN]")}` : "";
271
264
  console.error(
272
- `${color("ok", "\u2713")} launch ${adapter}${mobileTarget ? ` ${mobileTarget}` : ""} \u2014 ${color("ok", profile.deviceLabel(launch))} \xB7 ${tierNote} \xB7 app + bridge ready${devNote}`
265
+ `${color("ok", "\u2713")} launch ${adapter}${platformTarget ? ` ${platformTarget}` : ""} \u2014 ${color("ok", profile.deviceLabel(launch))} \xB7 ${tierNote} \xB7 app + bridge ready${devNote}`
273
266
  );
274
267
  }
275
268
  return EXIT.ok;
276
269
  }
277
270
  function launchFail(launch, profile, state, failure) {
278
- const { adapter, mobileTarget, target, stream, tier } = launch;
271
+ const { adapter, platformTarget, target, stream, tier } = launch;
279
272
  const userAction = failure.userAction ?? `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`;
280
273
  if (stream.enabled) {
281
274
  for (const mutation of state.mutations) stream.mutation(mutation);
@@ -289,7 +282,7 @@ function launchFail(launch, profile, state, failure) {
289
282
  });
290
283
  stream.complete("fail", failure.exitCode, {
291
284
  adapter,
292
- platform: mobileTarget ?? null,
285
+ platform: platformTarget ?? null,
293
286
  tier,
294
287
  recovered: state.recovered,
295
288
  mutations: state.mutations,
@@ -304,7 +297,7 @@ function launchFail(launch, profile, state, failure) {
304
297
  schemaVersion: 1,
305
298
  command: "launch",
306
299
  adapter,
307
- platform: mobileTarget ?? null,
300
+ platform: platformTarget ?? null,
308
301
  tier,
309
302
  status: "fail",
310
303
  phase: state.attemptedRecoveries.length ? "recover" : "launch",
@@ -328,7 +321,7 @@ function launchFail(launch, profile, state, failure) {
328
321
  );
329
322
  } else {
330
323
  console.error(colorHumanMessage(
331
- `\u2717 launch ${adapter}${mobileTarget ? ` ${mobileTarget}` : ""} failed
324
+ `\u2717 launch ${adapter}${platformTarget ? ` ${platformTarget}` : ""} failed
332
325
  ${failure.message}` + (failure.originalError ? `
333
326
  --- original failure ---
334
327
  ${failure.originalError}` : "") + `
@@ -6,14 +6,9 @@ import path from "node:path";
6
6
  import {
7
7
  depsCheck,
8
8
  recordDepsBaseline
9
- } from "@farmslot/recipe-harness/runtime/deps-readiness";
10
- import { colorHumanMessage } from "../../cli-color.js";
11
- import {
12
- importRecipeHarnessRuntimeCdp,
13
- recipeHarnessPath,
14
- recipeRuntimeDir,
15
- runnerDir
16
- } from "../../paths.js";
9
+ } from "@farmslot/recipe-runner/runtime/deps-readiness";
10
+ import { colorHumanMessage, recipeHarnessPath, recipeRuntimeDir, EXIT, spawnScriptStreaming } from "@farmslot/recipe-cli/harness";
11
+ import { importRecipeHarnessRuntimeCdp, runnerDir } from "../../paths.js";
17
12
  import { extensionIdFromKey } from "../../adapters/extension/extension-id.js";
18
13
  import {
19
14
  EXTENSION_PRODUCT_CONFIG_FINGERPRINT_FILENAME,
@@ -29,7 +24,6 @@ import {
29
24
  import { checkExtensionRuntimeHealth } from "../../adapters/extension/runtime.js";
30
25
  import { ensureHarnessFresh } from "../../adapters/harness-freshness.js";
31
26
  import { isExtensionWatcherLive, stopExtensionWatcher } from "../../adapters/slot-ports.js";
32
- import { EXIT, spawnScriptStreaming } from "../shared.js";
33
27
  import { readExtensionPinnedFlags } from "../../adapters/extension/feature-flags.js";
34
28
  import { sameFeatureFlags } from "../../feature-flags.js";
35
29
  const { CdpSession } = await importRecipeHarnessRuntimeCdp();
@@ -1,3 +1,4 @@
1
+ import { EXIT } from "@farmslot/recipe-cli/harness";
1
2
  import { prepareMobile } from "../../adapters/mobile/prepare.js";
2
3
  import {
3
4
  mobileSourceCheck,
@@ -7,7 +8,6 @@ import { ensureHarnessFresh } from "../../adapters/harness-freshness.js";
7
8
  import { pinMobileFeatureFlags } from "../../adapters/mobile/feature-flags.js";
8
9
  import { toRemoteFlagSpec } from "../../feature-flags.js";
9
10
  import { clearMobileReleaseArtifactState } from "../../adapters/mobile/release-artifact-state.js";
10
- import { EXIT } from "../shared.js";
11
11
  async function launchMobile(target, mobileTarget, tier, json, restartApp = false, clearMetro = false, remoteFlags) {
12
12
  clearMobileReleaseArtifactState(target);
13
13
  await ensureHarnessFresh(target, "mobile");
@@ -2,9 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { resolveActionManifest } from "../manifest.js";
4
4
  import { importRecipeProtocol } from "../paths.js";
5
- import { color } from "../cli-color.js";
6
- import { EXIT } from "./shared.js";
7
- import { optionFlag, optionString, optionStrings, resolveAdapter, shellQuoteArg } from "./parse-args.js";
5
+ import { color, EXIT, optionFlag, optionString, optionStrings, resolveAdapter, shellQuoteArg } from "@farmslot/recipe-cli/harness";
8
6
  import { actionDomain, describeManifestActions, renderHumanActionCatalog } from "./manifest.js";
9
7
  import { shortActionNames } from "@farmslot/recipe-cli";
10
8
  import {
@@ -1,16 +1,16 @@
1
+ import { adapterDetectNext, EXIT, flag, parseFlags, resolveFlagsAdapter, spawnInherit, spawnScript, str, targetOf, usageOut } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { runnerDir } from "../paths.js";
4
5
  import { getAdapterSurface } from "../adapters/surface.js";
5
- import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnInherit, spawnScript, str, targetOf, usageOut } from "./shared.js";
6
6
  const LOGS_BOOLEANS = /* @__PURE__ */ new Set(["full", "json", "window"]);
7
7
  async function handleLogs(argv) {
8
8
  const { options } = parseFlags(argv, LOGS_BOOLEANS);
9
9
  const json = flag(options, "json");
10
10
  const target = targetOf(options);
11
- const adapter = resolveAdapter(options, target);
11
+ const adapter = resolveFlagsAdapter(options, target);
12
12
  if (!adapter) {
13
- return usageOut(json, "logs", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
13
+ return usageOut(json, "logs", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
14
14
  }
15
15
  const surface = getAdapterSurface(adapter);
16
16
  if (surface.headless) {
@@ -1,22 +1,11 @@
1
1
  import { resolveActionManifest } from "../manifest.js";
2
- import { color } from "../cli-color.js";
3
- import { EXIT } from "./shared.js";
4
- import {
5
- isRecord,
6
- optionFlag,
7
- optionString,
8
- optionStrings,
9
- resolveAdapter,
10
- shellQuoteArg
11
- } from "./parse-args.js";
2
+ import { closest, color, isSensitiveKey, redactStructuredValue, EXIT, isRecord, optionFlag, optionString, optionStrings, resolveAdapter, shellQuoteArg } from "@farmslot/recipe-cli/harness";
12
3
  import { resolveMetaMaskLibrarySources } from "./run-engine.js";
13
- import { isSensitiveKey, redactStructuredValue } from "../command-journal.js";
14
4
  import {
15
5
  OFFICIAL_RECIPE_ACTIONS,
16
6
  officialRecipeActionCapabilities
17
7
  } from "@farmslot/protocol";
18
8
  import { metaMaskActionExecutionCapabilities } from "../recipe-security.js";
19
- import { closest } from "../command-contract.js";
20
9
  import {
21
10
  actionCapabilityMatrix,
22
11
  actionCategory,
@@ -1,6 +1,8 @@
1
1
  import { execFileSync } from "node:child_process";
2
- import { resolveMobileToolPath } from "../../library/actions/mobile/platform/tool-paths.mjs";
3
- import { listConnectedDevices } from "../devices.js";
2
+ import {
3
+ listConnectedDevices,
4
+ resolveMobileToolPath
5
+ } from "@farmslot/adapter-rn";
4
6
  import { renderDeviceList, scopedDevices } from "./device-target.js";
5
7
  import { probeMobileLiveState } from "./status-probe.js";
6
8
  function mobileDeviceView(allDevices) {
@@ -0,0 +1,110 @@
1
+ import {
2
+ argValue,
3
+ hasArg,
4
+ shellQuoteArg
5
+ } from "@farmslot/recipe-cli/harness";
6
+ import { getAdapterSurface } from "../adapters/surface.js";
7
+ const USAGE = `mm-harness \u2014 install and validate the MetaMask recipe runtime in a checkout.
8
+
9
+ Run it from inside a MetaMask checkout; the platform (mobile | extension | core | terminal)
10
+ is auto-detected from the repo. Pass --platform only to override.
11
+
12
+ Commands (one copy-pasteable example each):
13
+ install Install the recipe harness runtime overlay into the checkout.
14
+ mm-harness install
15
+ verify Check the harness/runtime is present and healthy (no app launch).
16
+ mm-harness verify
17
+ cleanup Remove the installed harness overlay and restore the checkout.
18
+ mm-harness cleanup
19
+
20
+ Options:
21
+ --platform <mobile|extension|core|terminal> Override auto-detection (alias: --adapter).
22
+ --target <repo> Checkout to operate on (default: current dir).
23
+ --json Machine-readable summary for agents/scripts.
24
+ -- <args> Forward the rest verbatim to the underlying
25
+ script, e.g. -- --platform ios --preflight-mode fast
26
+ (mobile) or -- --cdp-port 6665 (extension).
27
+
28
+ Examples:
29
+ mm-harness install # inside a checkout, auto-detected
30
+ mm-harness verify --platform extension
31
+ mm-harness cleanup --target ../metamask-mobile
32
+
33
+ core supports install, verify, and cleanup (headless; no live).`;
34
+ const overlayCommandOptions = {
35
+ usage: USAGE,
36
+ install: ({ adapter, target, forward, json }) => hasArg(forward, "--runway") ? handleRunwayInstall(adapter, target, forward, json) : void 0
37
+ };
38
+ async function handleRunwayInstall(adapter, target, forward, json) {
39
+ const rerunCommand = runwayInstallRerunCommand(adapter, target, forward, json);
40
+ const surface = getAdapterSurface(adapter);
41
+ const runtimeDir = argValue(forward, "--runtime-dir");
42
+ const watcherPort = argValue(forward, "--watcher-port");
43
+ const result = await surface.runwayProvision.run(target, {
44
+ json,
45
+ platform: argValue(forward, "--platform") ?? "ios",
46
+ branch: argValue(forward, "--branch"),
47
+ defaultBranch: argValue(forward, "--default-branch"),
48
+ run: argValue(forward, "--run"),
49
+ cacheRoot: argValue(forward, "--cache-root"),
50
+ simulator: argValue(forward, "--simulator") ?? argValue(forward, "--device"),
51
+ runtime: argValue(forward, "--runtime"),
52
+ deviceType: argValue(forward, "--device-type"),
53
+ slot: argValue(forward, "--slot"),
54
+ watcherPort,
55
+ runtimeDir,
56
+ force: hasArg(forward, "--force"),
57
+ resolveOnly: hasArg(forward, "--resolve-only"),
58
+ rerunCommand
59
+ });
60
+ const provisionedSimulator = typeof result.simulator === "object" && result.simulator ? result.simulator : void 0;
61
+ const provisionedDevice = typeof provisionedSimulator?.udid === "string" ? provisionedSimulator.udid : typeof provisionedSimulator?.name === "string" ? provisionedSimulator.name : void 0;
62
+ const next = result.status === "pass" && surface.runwayProvision.launchNext && result.resolveOnly !== true ? `mm-harness launch ${String(result.platform ?? "ios")} --adapter ${adapter} --target ${shellQuoteArg(target)}` + (provisionedDevice ? ` --device ${shellQuoteArg(provisionedDevice)}` : "") : void 0;
63
+ const output = next ? { ...result, next } : result;
64
+ if (json) {
65
+ console.log(JSON.stringify(output, null, 2));
66
+ } else if (result.status === "pass") {
67
+ const cache = typeof result.cache === "object" && result.cache ? result.cache : void 0;
68
+ const simulator = provisionedSimulator;
69
+ const artifact = typeof result.artifact === "object" && result.artifact ? result.artifact : void 0;
70
+ if (result.resolveOnly) {
71
+ console.error(`\u2713 resolved Runway app for ${adapter} ${result.platform ?? ""} run=${artifact?.runId ?? "unknown"} revision=${artifact?.revision ?? "unknown"} artifact=${artifact?.artifactName ?? "unknown"}`);
72
+ } else {
73
+ const action = result.skipped ? "already provisioned" : "installed Runway app";
74
+ console.error(`\u2713 ${action} for ${adapter} ${result.platform ?? ""} simulator=${simulator?.name ?? "unknown"} udid=${simulator?.udid ?? "unknown"} cache=${cache?.status ?? "skip"}`);
75
+ }
76
+ if (next) console.error(` Next: ${next}`);
77
+ } else {
78
+ console.error(`\u2717 mm-harness install --runway: ${result.error?.message ?? "runway install failed"}
79
+ Next: ${result.error?.userAction ?? rerunCommand}`);
80
+ }
81
+ return result.exitCode;
82
+ }
83
+ function runwayInstallRerunCommand(adapter, target, forward, json) {
84
+ const parts = ["mm-harness", "install", "--runway", "--adapter", adapter, "--target", shellQuoteArg(target)];
85
+ const valueFlags = [
86
+ "--platform",
87
+ "--branch",
88
+ "--default-branch",
89
+ "--run",
90
+ "--cache-root",
91
+ "--simulator",
92
+ "--device",
93
+ "--slot",
94
+ "--watcher-port",
95
+ "--runtime-dir",
96
+ "--runtime",
97
+ "--device-type"
98
+ ];
99
+ for (const flag of valueFlags) {
100
+ const value = argValue(forward, flag);
101
+ if (value) parts.push(flag, shellQuoteArg(value));
102
+ }
103
+ if (hasArg(forward, "--force")) parts.push("--force");
104
+ if (hasArg(forward, "--resolve-only")) parts.push("--resolve-only");
105
+ if (json) parts.push("--json");
106
+ return parts.join(" ");
107
+ }
108
+ export {
109
+ overlayCommandOptions
110
+ };
@@ -1,3 +1,4 @@
1
+ import { EXIT } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import crypto from "node:crypto";
3
4
  import fs from "node:fs";
@@ -6,7 +7,6 @@ import os from "node:os";
6
7
  import path from "node:path";
7
8
  import { isPin, PIN_MARKER } from "../harness-pin.js";
8
9
  import { runnerDir } from "../paths.js";
9
- import { EXIT } from "./shared.js";
10
10
  const PACKAGE_NAME = "@deeeed/metamask-harness";
11
11
  const STALE_STAGING_MS = 30 * 60 * 1e3;
12
12
  const RUNNER_STATE = /* @__PURE__ */ new Set([".omc", ".omx"]);
@@ -1,10 +1,10 @@
1
+ import { optionFlag, optionString, parseArgs, usageError } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { BODY_FILE, renderPrBody } from "../pr-body/render.js";
4
- import { optionFlag, optionString, parseArgs, usageError } from "./parse-args.js";
5
5
  const USAGE = "Usage: mm-harness pr-body render <task-dir> [--command <text>] [--out <file>] [--json]";
6
6
  async function handlePrBody(argv) {
7
- const parsed = parseArgs(argv, "pr-body");
7
+ const parsed = parseArgs(argv);
8
8
  const [action, taskArg] = parsed.positional;
9
9
  if (action !== "render" || !taskArg) throw usageError(USAGE);
10
10
  const taskDir = path.resolve(taskArg);
@@ -1,20 +1,15 @@
1
- import { acquireCheckoutLock, trackCheckoutChild } from "../checkout-lock.js";
2
- import { recordCommandStage } from "../command-journal.js";
1
+ import { acquireCheckoutLock, trackCheckoutChild, recordCommandStage, color, stripAnsi, harnessExecutable, recipeRuntimeDir, detectAdapter, optionFlag, optionString, parseArgs, targetPath, usageError, EXIT } from "@farmslot/recipe-cli/harness";
3
2
  import { spawn } from "node:child_process";
4
- import { OperationOutputTail } from "@farmslot/recipe-harness/runtime/operation";
3
+ import { OperationOutputTail } from "@farmslot/recipe-runner/runtime/operation";
5
4
  import fs from "node:fs";
6
5
  import path from "node:path";
7
6
  import {
8
7
  READINESS_RECORD
9
8
  } from "@farmslot/protocol";
10
- import { color, stripAnsi } from "../cli-color.js";
11
9
  import { runnerProvenance } from "../doctor.js";
12
- import { detectAdapter } from "../harness.js";
13
10
  import { adapterRegistry } from "../adapters/registry.js";
14
11
  import { getAdapterSurface } from "../adapters/surface.js";
15
- import { harnessExecutable, PREPARE_PROGRESS_ARTIFACT, recipeRuntimeDir } from "../paths.js";
16
- import { optionFlag, optionString, parseArgs, targetPath, usageError } from "./parse-args.js";
17
- import { EXIT } from "./shared.js";
12
+ import { PREPARE_PROGRESS_ARTIFACT } from "../paths.js";
18
13
  import { formatDuration } from "./status-watch.js";
19
14
  import { FeatureFlagSpecError, parseRemoteFlagSpec } from "../feature-flags.js";
20
15
  const USAGE = "mm-harness prepare [--target <checkout>] [--platform mobile|extension|core] [--mobile-platform ios|android] [--clear-metro] [--artifacts-dir <dir>] [--json]";
@@ -282,7 +277,7 @@ function resolvePlatform(requested, target) {
282
277
  );
283
278
  }
284
279
  async function handlePrepare(argv) {
285
- const { options } = parseArgs(argv, "prepare");
280
+ const { options } = parseArgs(argv);
286
281
  const target = targetPath(options);
287
282
  if (!fs.existsSync(target)) return handlePrepareLocked(argv);
288
283
  const lock = acquireCheckoutLock(target, "prepare");
@@ -298,11 +293,11 @@ async function handlePrepare(argv) {
298
293
  }
299
294
  }
300
295
  async function handlePrepareLocked(argv) {
301
- const { options } = parseArgs(argv, "prepare");
296
+ const { options } = parseArgs(argv);
302
297
  const json = optionFlag(options, "json");
303
298
  const target = targetPath(options);
304
299
  const surface = resolvePlatform(optionString(options, "platform"), target);
305
- const platform = surface.adapter;
300
+ const platform = surface.id;
306
301
  const artifactsDir = path.resolve(
307
302
  optionString(options, "artifactsDir") ?? path.join(target, recipeRuntimeDir())
308
303
  );
@@ -1,27 +1,18 @@
1
- import { detectAdapter } from "../harness.js";
1
+ import { detectAdapter, adapterDetectNext, usageOut, applyWatcherPortOption, optionFlag, optionString, shellQuote, shellQuoteArg, targetPath } from "@farmslot/recipe-cli/harness";
2
2
  import { assertAdapter } from "../paths.js";
3
3
  import { getAdapterSurface } from "../adapters/surface.js";
4
- import { ADAPTER_DETECT_NEXT, usageOut } from "./shared.js";
5
- import {
6
- applyWatcherPortOption,
7
- optionFlag,
8
- optionString,
9
- shellQuote,
10
- shellQuoteArg,
11
- targetPath
12
- } from "./parse-args.js";
13
4
  async function handleProvision({ positional, options, rawArgv }) {
14
5
  applyWatcherPortOption(options);
15
6
  const json = optionFlag(options, "json");
16
7
  const target = targetPath(options);
17
8
  const adapter = optionString(options, "adapter") ?? detectAdapter(target);
18
9
  if (!adapter) {
19
- return usageOut(json, "provision", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
10
+ return usageOut(json, "provision", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
20
11
  }
21
12
  try {
22
13
  assertAdapter(adapter);
23
14
  } catch (error) {
24
- return usageOut(json, "provision", error instanceof Error ? error.message : String(error), ADAPTER_DETECT_NEXT);
15
+ return usageOut(json, "provision", error instanceof Error ? error.message : String(error), adapterDetectNext());
25
16
  }
26
17
  const surface = getAdapterSurface(adapter);
27
18
  const rerunCommand = provisionRerunCommand(rawArgv, options, adapter, target);
@@ -1,10 +1,9 @@
1
+ import { CliError, isRecord, optionFlag, optionString, usageError, EXIT } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { adviseRecipe } from "../recipe-assistance.js";
4
5
  import { resolveAdvisorPolicy } from "../advisor-policy.js";
5
6
  import { RECIPE_ADVICE_RUBRIC, RECIPE_PLANNING_RUBRIC } from "../recipe-assistance-rubric.js";
6
- import { CliError, isRecord, optionFlag, optionString, usageError } from "./parse-args.js";
7
- import { EXIT } from "./shared.js";
8
7
  async function handleRecipeAdvice(options, mode = "review") {
9
8
  try {
10
9
  return await runRecipeAdvice(options, mode);
@@ -1,10 +1,9 @@
1
+ import { EXIT, usageOut, optionFlag, optionString, parseArgs } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import {
4
5
  buildRecipeQualityArtifact
5
6
  } from "@farmslot/agent-runtime";
6
- import { EXIT, usageOut } from "./shared.js";
7
- import { optionFlag, optionString, parseArgs } from "./parse-args.js";
8
7
  const BUILD_USAGE = "mm-harness recipe-quality build --input <compact.json> --output <path> [--json]";
9
8
  const SHORTHAND_USAGE = "mm-harness recipe-quality build --input <compact.json> --output <path> [--json]\n Shorthand: mm-harness recipe-quality --input <compact.json> --output <path> [--json]\n Note: apply the quality bar from mm-harness help, then build its compact verdict here.";
10
9
  function errorMessage(error) {
@@ -34,7 +33,7 @@ function invalidOut(json, message, source) {
34
33
  return EXIT.validation;
35
34
  }
36
35
  async function handleRecipeQuality(argv) {
37
- const { positional, options } = parseArgs(argv, "recipe-quality");
36
+ const { positional, options } = parseArgs(argv);
38
37
  const json = optionFlag(options, "json");
39
38
  const action = positional[0];
40
39
  if (action === "advise" || action === "plan" || action === "advisor-status") {
@@ -1,11 +1,5 @@
1
+ import { adapterOption, optionFlag, optionString, parsePort, targetPath } from "@farmslot/recipe-cli/harness";
1
2
  import { resolveExtensionId } from "../adapters/extension/extension-id.js";
2
- import {
3
- adapterOption,
4
- optionFlag,
5
- optionString,
6
- parsePort,
7
- targetPath
8
- } from "./parse-args.js";
9
3
  async function handleResolveExtension({ options }) {
10
4
  const adapter = adapterOption(options);
11
5
  if (adapter !== "extension") throw new Error("resolve-extension currently applies to the extension adapter.");
@@ -1,6 +1,6 @@
1
+ import { detectAdapter, optionFlag, optionString, parseArgs, targetPath, usageError } from "@farmslot/recipe-cli/harness";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
- import { detectAdapter } from "../harness.js";
4
4
  import { runnerDir } from "../paths.js";
5
5
  import {
6
6
  configPath,
@@ -15,7 +15,7 @@ import {
15
15
  resolveReference,
16
16
  defaultBaseRef
17
17
  } from "../review/knowledge.js";
18
- import { optionFlag, optionString, parseArgs, targetPath, usageError } from "./parse-args.js";
18
+ import "../adapters/surface.js";
19
19
  const EXIT_OK = 0;
20
20
  let cachedHarnessVersion;
21
21
  function harnessVersion() {
@@ -54,7 +54,7 @@ function resolveReviewContext(options, { fetch = true } = {}) {
54
54
  return context;
55
55
  }
56
56
  function handleHelpReview(argv, harnessVersion2) {
57
- const parsed = parseArgs(argv, "help");
57
+ const parsed = parseArgs(argv);
58
58
  const context = resolveReviewContext(parsed.options, { fetch: false });
59
59
  const input = {
60
60
  adapter: context.adapter,
@@ -73,7 +73,7 @@ function handleHelpReview(argv, harnessVersion2) {
73
73
  return EXIT_OK;
74
74
  }
75
75
  async function handleReview(argv, version = harnessVersion()) {
76
- const parsed = parseArgs(argv, "review");
76
+ const parsed = parseArgs(argv);
77
77
  const subcommand = parsed.positional[0];
78
78
  if (subcommand !== "checklist") {
79
79
  throw usageError("Usage: mm-harness review checklist [--domain <name>] [--since <sha>] [--base <ref>] [--out <file>] [--json]");