@deeeed/metamask-harness 0.79.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 (210) hide show
  1. package/CHANGELOG.md +47 -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/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/bin/mm-harness +9 -28
  26. package/dist/adapters/checkout-files.js +16 -0
  27. package/dist/adapters/console-capture.js +14 -1
  28. package/dist/adapters/core/actions.js +4 -3
  29. package/dist/adapters/core/overlay.js +1 -1
  30. package/dist/adapters/core/readiness.js +28 -1
  31. package/dist/adapters/core/run.js +2 -2
  32. package/dist/adapters/core/surface.js +16 -1
  33. package/dist/adapters/extension/browser-pid.js +56 -0
  34. package/dist/adapters/extension/console-capture.js +1 -1
  35. package/dist/adapters/extension/doctor-checks.js +1 -1
  36. package/dist/adapters/extension/ensure-ready.js +5 -4
  37. package/dist/adapters/extension/extension-id.js +1 -1
  38. package/dist/adapters/extension/feature-flags.js +1 -1
  39. package/dist/adapters/extension/fixtures.js +1 -2
  40. package/dist/adapters/extension/launch.js +4 -5
  41. package/dist/adapters/extension/launched-browser.js +1 -2
  42. package/dist/adapters/extension/network-observer.js +1 -1
  43. package/dist/adapters/extension/overlay.js +1 -1
  44. package/dist/adapters/extension/product-config.js +1 -1
  45. package/dist/adapters/extension/readiness.js +1 -1
  46. package/dist/adapters/extension/reload.js +1 -1
  47. package/dist/adapters/extension/run.js +24 -10
  48. package/dist/adapters/extension/runtime-decision.js +2 -1
  49. package/dist/adapters/extension/runtime.js +9 -5
  50. package/dist/adapters/extension/surface.js +15 -2
  51. package/dist/adapters/extension/verify.js +2 -2
  52. package/dist/adapters/harness-freshness.js +2 -1
  53. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  54. package/dist/adapters/mobile/app-identity.js +10 -0
  55. package/dist/adapters/mobile/doctor-checks.js +1 -1
  56. package/dist/adapters/mobile/fixtures.js +1 -1
  57. package/dist/adapters/mobile/launch.js +27 -28
  58. package/dist/adapters/mobile/lifecycle.js +1 -1
  59. package/dist/adapters/mobile/metro-env.js +9 -59
  60. package/dist/adapters/mobile/performance-observer.js +1 -1
  61. package/dist/adapters/mobile/prepare-target.js +1 -1
  62. package/dist/adapters/mobile/prepare.js +3 -4
  63. package/dist/adapters/mobile/provision.js +1 -1
  64. package/dist/adapters/mobile/readiness.js +1 -1
  65. package/dist/adapters/mobile/recording-target.js +15 -0
  66. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  67. package/dist/adapters/mobile/reload.js +1 -1
  68. package/dist/adapters/mobile/run.js +8 -7
  69. package/dist/adapters/mobile/runtime-decision.js +2 -4
  70. package/dist/adapters/mobile/source-freshness.js +22 -126
  71. package/dist/adapters/mobile/surface.js +26 -3
  72. package/dist/adapters/mobile/ui.js +16 -5
  73. package/dist/adapters/mobile/verify.js +2 -3
  74. package/dist/adapters/performance/cdp-trace.js +1 -1
  75. package/dist/adapters/performance/device-platform.js +1 -1
  76. package/dist/adapters/slot-ports.js +1 -1
  77. package/dist/adapters/surface.js +1 -0
  78. package/dist/adapters/terminal/console-capture.js +1 -1
  79. package/dist/adapters/terminal/launch.js +1 -1
  80. package/dist/adapters/terminal/run.js +25 -2
  81. package/dist/adapters/terminal/surface.js +7 -1
  82. package/dist/adapters.js +8 -4
  83. package/dist/advisor-policy.js +1 -1
  84. package/dist/checkout-view.js +1 -2
  85. package/dist/cli.js +28 -15
  86. package/dist/command-contract.js +1 -26
  87. package/dist/commands/call.js +10 -24
  88. package/dist/commands/check.js +3 -4
  89. package/dist/commands/checklist.js +1 -1
  90. package/dist/commands/completion-candidates.js +2 -10
  91. package/dist/commands/config.js +2 -2
  92. package/dist/commands/core-readiness.js +12 -7
  93. package/dist/commands/debug.js +3 -3
  94. package/dist/commands/device-target.js +5 -5
  95. package/dist/commands/doctor.js +2 -14
  96. package/dist/commands/domain.js +2 -2
  97. package/dist/commands/ensure-ready.js +1 -7
  98. package/dist/commands/fixtures.js +5 -6
  99. package/dist/commands/help.js +3 -4
  100. package/dist/commands/launch/device.js +10 -17
  101. package/dist/commands/launch/extension.js +2 -8
  102. package/dist/commands/launch/mobile.js +1 -1
  103. package/dist/commands/list-executables.js +1 -3
  104. package/dist/commands/logs.js +3 -3
  105. package/dist/commands/manifest.js +1 -12
  106. package/dist/commands/mobile-device-view.js +4 -2
  107. package/dist/commands/overlay.js +110 -0
  108. package/dist/commands/pin.js +1 -1
  109. package/dist/commands/pr-body.js +2 -2
  110. package/dist/commands/prepare.js +4 -9
  111. package/dist/commands/provision.js +3 -12
  112. package/dist/commands/recipe-advice.js +1 -2
  113. package/dist/commands/recipe-quality.js +2 -3
  114. package/dist/commands/resolve-extension.js +1 -7
  115. package/dist/commands/review.js +4 -4
  116. package/dist/commands/run-engine.js +12 -51
  117. package/dist/commands/run.js +4 -25
  118. package/dist/commands/runtime-decision.js +1 -6
  119. package/dist/commands/runtime-health.js +1 -7
  120. package/dist/commands/runtime-launch.js +1 -13
  121. package/dist/commands/self-test.js +2 -6
  122. package/dist/commands/status-watch.js +1 -2
  123. package/dist/commands/status.js +2 -5
  124. package/dist/commands/task-init.js +2 -3
  125. package/dist/commands/tutorial.js +2 -2
  126. package/dist/commands/update.js +1 -1
  127. package/dist/completions-cache.js +1 -1
  128. package/dist/console-classifier.js +19 -0
  129. package/dist/doctor.js +2 -3
  130. package/dist/funding-execution-context.js +15 -1
  131. package/dist/host.js +32 -0
  132. package/dist/index.js +1 -6
  133. package/dist/live-tsx.js +49 -0
  134. package/dist/mm-harness-cli.js +2 -4
  135. package/dist/network-observation.js +4 -4
  136. package/dist/paths.js +34 -64
  137. package/dist/performance-observation.js +2 -2
  138. package/dist/progress.js +1 -1
  139. package/dist/recipe-assistance-context.js +1 -3
  140. package/dist/recipe-security.js +2 -59
  141. package/dist/review/knowledge.js +2 -2
  142. package/dist/runner.js +5 -3
  143. package/dist/runtime-context.js +1 -1
  144. package/dist/task-view.js +2 -3
  145. package/docs/CONTRIBUTING.md +1 -1
  146. package/docs/cli-contract.md +2 -2
  147. package/docs/feature-guard.md +90 -0
  148. package/library/README.md +3 -1
  149. package/library/actions/core/perps/_controller.mjs +153 -130
  150. package/library/actions/core/perps/quote_fees.mjs +58 -0
  151. package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
  152. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  153. package/library/actions/extension/feature_flags/state.mjs +1 -1
  154. package/library/actions/extension/platform/cdp.mjs +2 -8
  155. package/library/actions/harness-exports.mjs +1 -6
  156. package/library/actions/mobile/app/network-control.mjs +6 -8
  157. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  158. package/library/actions/mobile/platform/bridge.mjs +8 -8
  159. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  160. package/library/actions/mobile/platform/native-session.mjs +3 -4
  161. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  162. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  163. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  164. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  165. package/library/actions/shared/console-findings.mjs +1 -1
  166. package/library/actions/terminal/platform/runtime.mjs +2 -1
  167. package/library/manifests/core.action-manifest.json +83 -13
  168. package/package.json +8 -9
  169. package/adapters/extension/lib/slot-title.cjs +0 -292
  170. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  171. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  172. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  173. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  174. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  175. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  176. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  177. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  178. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  179. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  180. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  181. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  182. package/adapters/mobile/launch-metro.cjs +0 -77
  183. package/adapters/mobile/metro-config.cjs +0 -98
  184. package/adapters/mobile/metro-log-generation.cjs +0 -106
  185. package/dist/adapters/mobile/frame-metrics.js +0 -118
  186. package/dist/adapters/mobile/video-recorder.js +0 -289
  187. package/dist/artifact-files.js +0 -92
  188. package/dist/checkout-lock.js +0 -149
  189. package/dist/cli-color.js +0 -98
  190. package/dist/command-journal.js +0 -308
  191. package/dist/commands/last.js +0 -60
  192. package/dist/commands/launch/index.js +0 -109
  193. package/dist/commands/parse-args.js +0 -279
  194. package/dist/commands/reload.js +0 -41
  195. package/dist/commands/run-report.js +0 -241
  196. package/dist/commands/shared.js +0 -323
  197. package/dist/commands/stop.js +0 -60
  198. package/dist/devices.js +0 -100
  199. package/dist/execution-provenance.js +0 -332
  200. package/dist/harness.js +0 -451
  201. package/dist/heal-bounds.js +0 -137
  202. package/dist/json-stream.js +0 -60
  203. package/dist/leaf-invoke.js +0 -19
  204. package/dist/library-provenance.js +0 -33
  205. package/dist/live-adapter-contract.js +0 -380
  206. package/dist/recording-target.js +0 -120
  207. package/dist/run-diagnostics.js +0 -364
  208. package/dist/run-recording.js +0 -481
  209. package/dist/runtime-proof.js +0 -90
  210. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
package/dist/cli.js CHANGED
@@ -1,5 +1,17 @@
1
+ import "./host.js";
2
+ import "./adapters/surface.js";
3
+ import {
4
+ detectAdapter,
5
+ handleHarness,
6
+ handleLast,
7
+ handleLaunch,
8
+ handleReload,
9
+ handleStop,
10
+ parseArgs,
11
+ targetPath
12
+ } from "@farmslot/recipe-cli/harness";
13
+ import { stopProjectCollector } from "../library/actions/shared/analytics/collector.mjs";
1
14
  import { adapterRegistry } from "./adapters/registry.js";
2
- import { detectAdapter, handleHarness } from "./harness.js";
3
15
  import { handleActions } from "./commands/manifest.js";
4
16
  import { handleDoctor } from "./commands/doctor.js";
5
17
  import { handleProvision } from "./commands/provision.js";
@@ -11,12 +23,9 @@ import { handleEnsureReady } from "./commands/ensure-ready.js";
11
23
  import { handleRun } from "./commands/run.js";
12
24
  import { handleSelfTest } from "./commands/self-test.js";
13
25
  import { handleCall } from "./commands/call.js";
14
- import { handleStop } from "./commands/stop.js";
15
26
  import { handleCompletionCandidates, invalidateCompletionCache } from "./commands/completion-candidates.js";
16
- import { handleLaunch } from "./commands/launch/index.js";
17
27
  import { handleLogs } from "./commands/logs.js";
18
28
  import { handleDebug } from "./commands/debug.js";
19
- import { handleReload } from "./commands/reload.js";
20
29
  import { handleFixtures } from "./commands/fixtures.js";
21
30
  import { handleRecipeQuality } from "./commands/recipe-quality.js";
22
31
  import { handleStatus } from "./commands/status.js";
@@ -25,12 +34,17 @@ import { handleChecklist } from "./commands/checklist.js";
25
34
  import { handleExecutionTemplate } from "./commands/execution-template.js";
26
35
  import { handlePrepare } from "./commands/prepare.js";
27
36
  import { handleTaskInit } from "./commands/task-init.js";
28
- import { handleLast } from "./commands/last.js";
29
37
  import { handleReview } from "./commands/review.js";
30
38
  import { handleDomain } from "./commands/domain.js";
31
39
  import { handleConfig } from "./commands/config.js";
32
40
  import { handlePrBody } from "./commands/pr-body.js";
33
- import { parseArgs, targetPath } from "./commands/parse-args.js";
41
+ import { overlayCommandOptions } from "./commands/overlay.js";
42
+ const stopOptions = {
43
+ companions: async (target) => {
44
+ const collector = await stopProjectCollector(target);
45
+ return collector.stopped && collector.pid !== null ? [{ label: "analytics collector", pid: collector.pid }] : [];
46
+ }
47
+ };
34
48
  const COMMANDS = {
35
49
  actions: handleActions,
36
50
  doctor: handleDoctor,
@@ -98,35 +112,34 @@ async function main(argv) {
98
112
  return command ? 0 : 2;
99
113
  }
100
114
  if (command === "status" || command === "health" || command === "home") {
101
- return handleStatus(parseArgs(argv.slice(1), "status"));
115
+ return handleStatus(parseArgs(argv.slice(1)));
102
116
  }
103
117
  if (command === "call") return handleCall(argv.slice(1));
104
118
  if (command === "completion-candidates") return handleCompletionCandidates(argv.slice(1));
105
- if (command === "provision") return handleProvision(parseArgs(argv.slice(1), command));
119
+ if (command === "provision") return handleProvision(parseArgs(argv.slice(1)));
106
120
  if (OVERLAY_COMMANDS.includes(command)) {
107
121
  if (command === "verify" && process.env.MM_HARNESS_SKIP_OVERLAY_ENSURE !== "1") {
108
122
  try {
109
- const { options } = parseArgs(argv.slice(1), command);
123
+ const { options } = parseArgs(argv.slice(1));
110
124
  const target = targetPath(options);
111
125
  const adapterRaw = (typeof options["adapter"] === "string" ? options["adapter"] : void 0) ?? (typeof options["platform"] === "string" ? options["platform"] : void 0) ?? detectAdapter(target);
112
- await import("./adapters/surface.js");
113
126
  if (adapterRaw && adapterRegistry.has(adapterRaw)) {
114
127
  await handleHarness(["install", "--platform", adapterRaw, "--target", target, "--quiet"]);
115
128
  }
116
129
  } catch {
117
130
  }
118
131
  }
119
- const code = await handleHarness(argv);
132
+ const code = await handleHarness(argv, overlayCommandOptions);
120
133
  if (code === 0 && command === "install") {
121
134
  try {
122
- invalidateCompletionCache(targetPath(parseArgs(argv.slice(1), command).options));
135
+ invalidateCompletionCache(targetPath(parseArgs(argv.slice(1)).options));
123
136
  } catch {
124
137
  }
125
138
  }
126
139
  return code;
127
140
  }
128
141
  if (command === "launch") return handleLaunch(argv.slice(1));
129
- if (command === "stop") return handleStop(argv.slice(1));
142
+ if (command === "stop") return handleStop(argv.slice(1), stopOptions);
130
143
  if (command === "logs") return handleLogs(argv.slice(1));
131
144
  if (command === "debug") return handleDebug(argv.slice(1));
132
145
  if (command === "reload") return handleReload(argv.slice(1));
@@ -137,14 +150,14 @@ async function main(argv) {
137
150
  if (command === "execution-template") return handleExecutionTemplate(argv.slice(1));
138
151
  if (command === "task") return handleTaskInit(argv.slice(1));
139
152
  if (command === "prepare") return handlePrepare(argv.slice(1));
140
- if (command === "last") return handleLast(parseArgs(argv.slice(1), command));
153
+ if (command === "last") return handleLast(parseArgs(argv.slice(1)));
141
154
  if (command === "review") return handleReview(argv.slice(1));
142
155
  if (command === "domain") return handleDomain(argv.slice(1));
143
156
  if (command === "config") return handleConfig(argv.slice(1));
144
157
  if (command === "pr-body") return handlePrBody(argv.slice(1));
145
158
  const handler = COMMANDS[command];
146
159
  if (!handler) throw new Error(`Unknown command: ${command}`);
147
- return handler(parseArgs(argv.slice(1), command));
160
+ return handler(parseArgs(argv.slice(1), command === "runtime-decision" ? { recordIsFlag: true } : {}));
148
161
  }
149
162
  if (!globalThis.__MM_HARNESS_WRAPPER__) {
150
163
  try {
@@ -1,3 +1,4 @@
1
+ import { closest } from "@farmslot/recipe-cli/harness";
1
2
  const bool = () => ({ kind: "boolean" });
2
3
  const value = (choices) => ({ kind: "value", ...choices ? { choices } : {} });
3
4
  const optionalValue = (choices) => ({ kind: "optional-value", ...choices ? { choices } : {} });
@@ -698,34 +699,8 @@ function actionFor(received, suggestion, example) {
698
699
  function exampleFor(command, examples) {
699
700
  return command ? examples[command] ?? `mm-harness ${command} --help` : "mm-harness --help";
700
701
  }
701
- function closest(input, candidates) {
702
- if (candidates.length === 0) return void 0;
703
- const ranked = candidates.map((candidate) => ({ candidate, distance: levenshtein(input, candidate) })).sort((left, right) => left.distance - right.distance || left.candidate.localeCompare(right.candidate));
704
- const best = ranked[0];
705
- if (!best) return void 0;
706
- const threshold = Math.max(2, Math.floor(Math.max(input.length, best.candidate.length) / 3));
707
- return best.distance <= threshold ? best.candidate : void 0;
708
- }
709
- function levenshtein(left, right) {
710
- let twoBack;
711
- let prior = Array.from({ length: right.length + 1 }, (_, index) => index);
712
- for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
713
- const current = [leftIndex];
714
- for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
715
- const substitution = prior[rightIndex - 1] + (left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1);
716
- current[rightIndex] = Math.min((current[rightIndex - 1] ?? 0) + 1, (prior[rightIndex] ?? 0) + 1, substitution);
717
- if (twoBack && leftIndex > 1 && rightIndex > 1 && left[leftIndex - 1] === right[rightIndex - 2] && left[leftIndex - 2] === right[rightIndex - 1]) {
718
- current[rightIndex] = Math.min(current[rightIndex] ?? 0, (twoBack[rightIndex - 2] ?? 0) + 1);
719
- }
720
- }
721
- twoBack = prior;
722
- prior = current;
723
- }
724
- return prior[right.length] ?? right.length;
725
- }
726
702
  export {
727
703
  PUBLIC_COMMAND_CONTRACTS,
728
- closest,
729
704
  publicCommandNames,
730
705
  publicCommandTokens,
731
706
  validatePublicInvocation
@@ -3,9 +3,7 @@ import { randomUUID } from "node:crypto";
3
3
  import path from "node:path";
4
4
  import { resolveActionManifest } from "../manifest.js";
5
5
  import { importRecipeProtocol } from "../paths.js";
6
- import { conciseFailureForHuman, recipeRunning } from "../heal-bounds.js";
7
- import { color } from "../cli-color.js";
8
- import { checkoutBusyOut, EXIT, usageOut } from "./shared.js";
6
+ import { color, acquireCheckoutLock, isSensitiveKey, recordCommandEvidence, redactStructuredValue, conciseFailureForHuman, recipeRunning, checkoutBusyOut, EXIT, usageOut, parseArgs, isRecord, optionFlag, optionString, optionStrings, resolveAdapter, shellQuote, usageError } from "@farmslot/recipe-cli/harness";
9
7
  import {
10
8
  actionLibraryContextArgs,
11
9
  actionExampleCommand,
@@ -13,17 +11,6 @@ import {
13
11
  resolveActionCapabilityMatrix
14
12
  } from "./manifest.js";
15
13
  import { fuzzyResolveActions, resolveActionCapabilityRefusal } from "@farmslot/recipe-cli";
16
- import {
17
- parseArgs,
18
- isRecord,
19
- optionFlag,
20
- optionString,
21
- optionStrings,
22
- resolveAdapter,
23
- runtimeOptionsFromCli,
24
- shellQuote,
25
- usageError
26
- } from "./parse-args.js";
27
14
  import { getAdapterSurface } from "../adapters/surface.js";
28
15
  import { handleListExecutables } from "./list-executables.js";
29
16
  import { applyDeviceTargeting } from "./device-target.js";
@@ -38,21 +25,17 @@ import {
38
25
  synthesizeOneNodeRecipe,
39
26
  validateRecipeAdapterAware
40
27
  } from "./run-engine.js";
41
- import { acquireCheckoutLock } from "../checkout-lock.js";
42
- import { formatRunDiagnosticsForHuman, readRunDiagnosticsDocument } from "../run-diagnostics.js";
43
28
  import {
44
29
  startRunNetworkObservation
45
30
  } from "../network-observation.js";
46
31
  import {
47
32
  startRunPerformanceObservation
48
33
  } from "../performance-observation.js";
49
- import { recipeTrustFailure } from "../recipe-security.js";
50
- import { isSensitiveKey, recordCommandEvidence, redactStructuredValue } from "../command-journal.js";
51
- import { closest } from "../command-contract.js";
52
- import { ProvenanceDriftError } from "../execution-provenance.js";
34
+ import { closest, formatRunDiagnosticsForHuman, ProvenanceDriftError, readRunDiagnosticsDocument, recipeRunOptionsFromCli, recipeTrustFailure } from "@farmslot/recipe-cli/harness";
35
+ import { fundingOptionsFromCli } from "../funding-execution-context.js";
53
36
  async function handleCall(argv) {
54
37
  if (argv.includes("--list")) {
55
- const { options: options2 } = parseArgs(argv, "call");
38
+ const { options: options2 } = parseArgs(argv);
56
39
  return handleListExecutables("call", options2);
57
40
  }
58
41
  if (argv.length > 0 && argv[0].startsWith("--")) {
@@ -61,7 +44,7 @@ async function handleCall(argv) {
61
44
  return EXIT.usage;
62
45
  }
63
46
  const { action: shortName, args, rest } = parseCallArgs(argv);
64
- const { options } = parseArgs(rest, "call");
47
+ const { options } = parseArgs(rest);
65
48
  if (["domain", "source", "sort"].some((name) => optionString(options, name) !== void 0)) {
66
49
  throw usageError("--domain, --source, and --sort require call --list.");
67
50
  }
@@ -214,7 +197,10 @@ async function handleCall(argv) {
214
197
  }
215
198
  const artifactsDir = optionString(options, "artifactsDir") ?? defaultCallArtifactsDir(target, resolvedAction);
216
199
  recordCommandEvidence(artifactsDir);
217
- const requestedRuntimeOptions = runtimeOptionsFromCli(options);
200
+ const requestedRuntimeOptions = {
201
+ ...recipeRunOptionsFromCli(adapter, options),
202
+ ...fundingOptionsFromCli(options)
203
+ };
218
204
  const inheritedSource = process.env.FARMSLOT_RECIPE_SOURCE_TRUST || process.env.FARMSLOT_RECIPE_SOURCE_KIND || process.env.FARMSLOT_RECIPE_SOURCE_NAME || process.env.FARMSLOT_RECIPE_SOURCE_DIGEST;
219
205
  let networkObservation;
220
206
  let performanceObservation;
@@ -509,7 +495,7 @@ function redactCallValue(value, key = "") {
509
495
  }
510
496
  async function handleCallHelp(argv, genericHelp) {
511
497
  const { action: shortName, rest } = parseCallArgs(argv.filter((arg) => arg !== "--help" && arg !== "-h"));
512
- const { options } = parseArgs(rest, "call");
498
+ const { options } = parseArgs(rest);
513
499
  let adapter;
514
500
  let manifest;
515
501
  let actionSources = /* @__PURE__ */ new Map();
@@ -1,16 +1,15 @@
1
+ import { detectAdapter, EXIT, usageOut, optionFlag, optionString, parseArgs, shellQuote } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
- import { detectAdapter } from "../harness.js";
5
- import { EXIT, usageOut } from "./shared.js";
6
- import { optionFlag, optionString, parseArgs, shellQuote } from "./parse-args.js";
5
+ import "../adapters/surface.js";
7
6
  const USAGE = "mm-harness check diff [--fix] [--target <repo>] [--adapter mobile|extension|core] [--base <ref>] [--profile fast|full] [--artifacts-dir <dir>] [--json]";
8
7
  const JS_TS_EXT_RE = /\.(?:c|m)?jsx?$|\.tsx?$/u;
9
8
  const FORMAT_EXT_RE = /\.(?:c|m)?jsx?$|\.tsx?$|\.json$|\.md$|\.css$|\.scss$|\.ya?ml$/u;
10
9
  const TEST_FILE_RE = /(?:^|[./_-])(?:test|spec)\.(?:c|m)?jsx?$|(?:^|[./_-])(?:test|spec)\.tsx?$/u;
11
10
  const ESLINT_SUPPRESSION_RE = /^\s*eslint-disable(?:-line|-next-line)?\b/u;
12
11
  async function handleCheck(argv) {
13
- const { positional, options } = parseArgs(argv, "check");
12
+ const { positional, options } = parseArgs(argv);
14
13
  const action = positional[0];
15
14
  const json = optionFlag(options, "json");
16
15
  const fix = optionFlag(options, "fix");
@@ -1,10 +1,10 @@
1
+ import { EXIT } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } 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
6
  import { HANDOFF_INPUT } from "@farmslot/agent-runtime";
6
7
  import { enumerateChecklistCheckboxes, isTerminalWorkerSignalStatus } from "@farmslot/protocol";
7
- import { EXIT } from "./shared.js";
8
8
  const require2 = createRequire(import.meta.url);
9
9
  const MARK_SCRIPT = require2.resolve(
10
10
  "@farmslot/agent-runtime/scripts/mark-checklist-step.cjs"
@@ -1,3 +1,4 @@
1
+ import { EXIT, parseArgs, optionFlag, optionString, optionStrings, resolveAdapter, targetPath } from "@farmslot/recipe-cli/harness";
1
2
  import { resolveActionManifest } from "../manifest.js";
2
3
  import {
3
4
  invalidateCompletionCache,
@@ -5,19 +6,10 @@ import {
5
6
  writeCompletionCandidates
6
7
  } from "../completions-cache.js";
7
8
  import { importRecipeProtocol } from "../paths.js";
8
- import { EXIT } from "./shared.js";
9
- import {
10
- parseArgs,
11
- optionFlag,
12
- optionString,
13
- optionStrings,
14
- resolveAdapter,
15
- targetPath
16
- } from "./parse-args.js";
17
9
  import { listRunnableRecipes, resolveMetaMaskLibrarySources } from "./run-engine.js";
18
10
  async function handleCompletionCandidates(argv) {
19
11
  const kind = argv[0];
20
- const { options } = parseArgs(argv.slice(1), "completion-candidates");
12
+ const { options } = parseArgs(argv.slice(1));
21
13
  const json = optionFlag(options, "json");
22
14
  if (kind !== "actions" && kind !== "recipes") {
23
15
  console.error("completion-candidates requires <actions|recipes>.");
@@ -1,3 +1,4 @@
1
+ import { optionFlag, parseArgs, usageError } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
3
  import {
3
4
  configPath,
@@ -6,7 +7,6 @@ import {
6
7
  REVIEW_ADAPTERS,
7
8
  writeConfig
8
9
  } from "../review/knowledge.js";
9
- import { optionFlag, parseArgs, usageError } from "./parse-args.js";
10
10
  const USAGE = "Usage: mm-harness config <list|path|get <key>|set <key> <value>|unset <key>> [--json]\n keys: libraries.<name>, references.<mobile|extension|core|terminal>, advisor.<enabled|provider|model|stages>";
11
11
  function parseKey(raw) {
12
12
  if (!raw) throw usageError(USAGE);
@@ -36,7 +36,7 @@ function setValue(config, key, value) {
36
36
  else config[key.section] = section;
37
37
  }
38
38
  async function handleConfig(argv) {
39
- const parsed = parseArgs(argv, "config");
39
+ const parsed = parseArgs(argv);
40
40
  const json = optionFlag(parsed.options, "json");
41
41
  const [action, rawKey, rawValue] = parsed.positional;
42
42
  const expectedPositionals = { path: 1, list: 1, get: 2, set: 3, unset: 2 }[action ?? "list"];
@@ -1,12 +1,24 @@
1
1
  import { createRequire } from "node:module";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { resolveLiveTsx } from "../live-tsx.js";
4
5
  const requireFromHarness = createRequire(import.meta.url);
5
6
  const CORE_RUNTIME_DEPS = ["immer"];
6
7
  function coreDependencyBlock(target) {
7
8
  const resolved = path.resolve(target);
8
9
  const nodeLinker = readYarnNodeLinker(resolved);
9
10
  const installAction = `cd ${shellQuote(resolved)} && yarn install --immutable`;
11
+ if (!resolveLiveTsx(resolved)) {
12
+ return process.env.TSX_BIN ? {
13
+ code: "CORE_TS_RUNNER_MISSING",
14
+ message: `TSX_BIN is ${process.env.TSX_BIN}, which is not an executable on PATH or relative to the checkout.`,
15
+ userAction: "unset TSX_BIN to use the tsx mm-harness ships, or point it at an existing tsx"
16
+ } : {
17
+ code: "CORE_TS_RUNNER_MISSING",
18
+ message: "no tsx runtime found for core actions (mm-harness ships one; this install is missing it).",
19
+ userAction: "reinstall mm-harness (npm install -g @deeeed/metamask-harness), or set TSX_BIN"
20
+ };
21
+ }
10
22
  if (nodeLinker === "pnp") {
11
23
  if (!fs.existsSync(path.join(resolved, ".pnp.cjs"))) {
12
24
  return {
@@ -24,13 +36,6 @@ function coreDependencyBlock(target) {
24
36
  userAction: installAction
25
37
  };
26
38
  }
27
- if (!fs.existsSync(path.join(resolved, "node_modules/.bin/tsx"))) {
28
- return {
29
- code: "CORE_DEPS_INCOMPLETE",
30
- message: "core dependencies are incomplete (node_modules/.bin/tsx is missing).",
31
- userAction: installAction
32
- };
33
- }
34
39
  for (const dependency of CORE_RUNTIME_DEPS) {
35
40
  if (!canResolveFromTarget(dependency, resolved)) {
36
41
  return {
@@ -1,14 +1,14 @@
1
+ import { adapterDetectNext, EXIT, flag, parseFlags, resolveFlagsAdapter, spawnScript, targetOf, usageOut } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
3
  import { runnerDir } from "../paths.js";
3
4
  import { getAdapterSurface } from "../adapters/surface.js";
4
- import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnScript, targetOf, usageOut } from "./shared.js";
5
5
  const DEBUG_BOOLEANS = /* @__PURE__ */ new Set(["worker", "devMenu", "json", "noOpen"]);
6
6
  async function handleDebug(argv) {
7
7
  const { options } = parseFlags(argv, DEBUG_BOOLEANS);
8
8
  const json = flag(options, "json");
9
9
  const target = targetOf(options);
10
- const adapter = resolveAdapter(options, target);
11
- if (!adapter) return usageOut(json, "debug", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
10
+ const adapter = resolveFlagsAdapter(options, target);
11
+ if (!adapter) return usageOut(json, "debug", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
12
12
  const surface = getAdapterSurface(adapter);
13
13
  if (surface.headless) return usageOut(json, "debug", `${adapter} is headless; there is no debug console.`, surface.hints.relaunch);
14
14
  surface.resolveSlotPorts(target);
@@ -1,12 +1,12 @@
1
+ import { optionString, shellQuoteArg, targetPath } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
- import { resolveMobileToolPath } from "../../library/actions/mobile/platform/tool-paths.mjs";
3
+ import { resolveMobileToolPath } from "@farmslot/adapter-rn";
3
4
  import {
4
5
  findAvailableIosSimulatorById,
5
6
  listConnectedDevices
6
- } from "../devices.js";
7
+ } from "@farmslot/adapter-rn";
7
8
  import { getAdapterSurface } from "../adapters/surface.js";
8
9
  import { persistExplicitDevicePin } from "../runtime-context.js";
9
- import { optionString, shellQuoteArg, targetPath } from "./parse-args.js";
10
10
  const DEVICE_SELECT_HINT = "Pass --device <name|udid|serial> to select one target. Do not shut down other connected devices to force selection.";
11
11
  function normalizeDeviceName(value) {
12
12
  return value.replace(/_/gu, " ").trim();
@@ -27,7 +27,7 @@ function resolveAndroidModel(serial) {
27
27
  }
28
28
  function setAndroidDeviceEnv(id, fallbackName) {
29
29
  process.env.PLATFORM = "android";
30
- process.env.MM_HARNESS_EXPLICIT_PLATFORM = "android";
30
+ process.env.RECIPE_RN_EXPLICIT_PLATFORM = "android";
31
31
  process.env.ADB_SERIAL = id;
32
32
  process.env.ANDROID_SERIAL = id;
33
33
  process.env.ANDROID_DEVICE = id;
@@ -39,7 +39,7 @@ function setAndroidDeviceEnv(id, fallbackName) {
39
39
  }
40
40
  function setIosDeviceEnv(id, fallbackName) {
41
41
  process.env.PLATFORM = "ios";
42
- process.env.MM_HARNESS_EXPLICIT_PLATFORM = "ios";
42
+ process.env.RECIPE_RN_EXPLICIT_PLATFORM = "ios";
43
43
  process.env.IOS_SIMULATOR = fallbackName?.trim() || id;
44
44
  process.env.SIM_UDID = id;
45
45
  process.env.ADB_SERIAL = "";
@@ -2,28 +2,16 @@ import { execFileSync } from "node:child_process";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { featureFlagReport } from "../feature-flags.js";
5
- import { color } from "../cli-color.js";
5
+ import { color, acquireCheckoutLock, detectAdapter, resolveRuntimeContextPath, ensureOverlay, newHealState, recipeRunning, adapterDetectNext, checkoutBusyOut, EXIT, usageOut, actionManifestPathOption, applyRuntimeDirOption, optionFlag, optionString, shellQuote, targetPath } from "@farmslot/recipe-cli/harness";
6
6
  import { collectCheckoutView, renderCheckoutView } from "../checkout-view.js";
7
7
  import { createDoctorReport, fixtureSummary, requiredDoctorCheckSummary } from "../doctor.js";
8
- import { detectAdapter, resolveRuntimeContextPath } from "../harness.js";
9
8
  import { assertAdapter, runnerDir } from "../paths.js";
10
9
  import { getAdapterSurface } from "../adapters/surface.js";
11
10
  import { loadActionManifest, validateManifest } from "../manifest.js";
12
- import { ensureOverlay, newHealState, recipeRunning } from "../heal-bounds.js";
13
11
  import { applyDeviceTargeting } from "./device-target.js";
14
12
  import { farmslotReadyIndicator } from "./farmslot-ready.js";
15
13
  import { updateStatus } from "./update.js";
16
14
  import { ensureRuntimeContext } from "../runtime-context.js";
17
- import { acquireCheckoutLock } from "../checkout-lock.js";
18
- import { ADAPTER_DETECT_NEXT, checkoutBusyOut, EXIT, usageOut } from "./shared.js";
19
- import {
20
- actionManifestPathOption,
21
- applyRuntimeDirOption,
22
- optionFlag,
23
- optionString,
24
- shellQuote,
25
- targetPath
26
- } from "./parse-args.js";
27
15
  function applyDoctorRuntimePorts(options) {
28
16
  const cdpPort = optionString(options, "cdpPort");
29
17
  if (cdpPort) {
@@ -48,7 +36,7 @@ async function handleDoctor({ options }) {
48
36
  const explicitAdapter = optionString(options, "adapter") ?? (platformOption === "ios" || platformOption === "android" ? "mobile" : platformOption);
49
37
  const adapter = explicitAdapter ?? detectAdapter(target);
50
38
  if (!adapter) {
51
- return usageOut(json, "doctor", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
39
+ return usageOut(json, "doctor", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
52
40
  }
53
41
  assertAdapter(adapter);
54
42
  const { readiness } = getAdapterSurface(adapter);
@@ -1,3 +1,4 @@
1
+ import { optionFlag, optionString, parseArgs, targetPath, usageError } from "@farmslot/recipe-cli/harness";
1
2
  import {
2
3
  changedFiles,
3
4
  classifyDomain,
@@ -5,10 +6,9 @@ import {
5
6
  discoverLibraries,
6
7
  readConfig
7
8
  } from "../review/knowledge.js";
8
- import { optionFlag, optionString, parseArgs, targetPath, usageError } from "./parse-args.js";
9
9
  import { adapterFromOptions } from "./review.js";
10
10
  async function handleDomain(argv) {
11
- const parsed = parseArgs(argv, "domain");
11
+ const parsed = parseArgs(argv);
12
12
  const target = targetPath(parsed.options);
13
13
  const json = optionFlag(parsed.options, "json");
14
14
  const declared = optionString(parsed.options, "domain") ?? process.env.MM_HARNESS_DOMAIN;
@@ -1,11 +1,5 @@
1
+ import { adapterOption, optionFlag, optionString, parsePort, targetPath } from "@farmslot/recipe-cli/harness";
1
2
  import { ensureExtensionReady } from "../adapters/extension/ensure-ready.js";
2
- import {
3
- adapterOption,
4
- optionFlag,
5
- optionString,
6
- parsePort,
7
- targetPath
8
- } from "./parse-args.js";
9
3
  async function handleEnsureReady({ options }) {
10
4
  const adapter = adapterOption(options);
11
5
  if (adapter !== "extension") throw new Error("ensure-ready currently applies to the extension adapter.");
@@ -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}` : "") + `
@@ -7,13 +7,8 @@ import {
7
7
  depsCheck,
8
8
  recordDepsBaseline
9
9
  } from "@farmslot/recipe-runner/runtime/deps-readiness";
10
- import { colorHumanMessage } from "../../cli-color.js";
11
- import {
12
- importRecipeHarnessRuntimeCdp,
13
- recipeHarnessPath,
14
- recipeRuntimeDir,
15
- runnerDir
16
- } from "../../paths.js";
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();