@deeeed/metamask-harness 0.79.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +1 -1
  3. package/adapters/extension/ensure-browser.sh +2 -2
  4. package/adapters/extension/inject.mjs +0 -1
  5. package/adapters/extension/launch-browser.cjs +39 -613
  6. package/adapters/extension/readiness.mjs +2 -5
  7. package/adapters/extension/reattach.sh +2 -2
  8. package/adapters/extension/stamp-runtime-title.cjs +1 -1
  9. package/adapters/manifest.json +16 -64
  10. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
  11. package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
  12. package/adapters/mobile/inject.sh +16 -0
  13. package/adapters/mobile/launch-console-forwarder.cjs +1 -2
  14. package/adapters/mobile/open-device.sh +2 -2
  15. package/adapters/mobile/reload-app.mjs +4 -4
  16. package/adapters/mobile/reset-app-data.sh +1 -1
  17. package/adapters/mobile/start-console-forwarder.sh +3 -2
  18. package/adapters/mobile/start-metro.sh +17 -12
  19. package/adapters/mobile/wait-for-bridge.cjs +2 -2
  20. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  21. package/adapters/shared/open-debug.mjs +2 -2
  22. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  23. package/adapters/terminal/cleanup.mjs +2 -1
  24. package/adapters/terminal/inject.mjs +2 -1
  25. package/adapters/terminal/launch.mjs +17 -13
  26. package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
  27. package/adapters/terminal/lib/readiness.mjs +20 -12
  28. package/adapters/terminal/policy.mjs +35 -0
  29. package/adapters/terminal/verify.mjs +10 -3
  30. package/adapters/terminal/wallet-host.mjs +43 -225
  31. package/bin/mm-harness +9 -28
  32. package/dist/adapters/checkout-files.js +16 -0
  33. package/dist/adapters/console-capture.js +14 -1
  34. package/dist/adapters/core/actions.js +8 -4
  35. package/dist/adapters/core/overlay.js +1 -1
  36. package/dist/adapters/core/readiness.js +28 -1
  37. package/dist/adapters/core/run.js +2 -2
  38. package/dist/adapters/core/surface.js +16 -1
  39. package/dist/adapters/extension/actions.js +6 -2
  40. package/dist/adapters/extension/browser-pid.js +56 -0
  41. package/dist/adapters/extension/console-capture.js +1 -1
  42. package/dist/adapters/extension/doctor-checks.js +1 -1
  43. package/dist/adapters/extension/ensure-ready.js +5 -4
  44. package/dist/adapters/extension/extension-id.js +1 -1
  45. package/dist/adapters/extension/feature-flags.js +1 -1
  46. package/dist/adapters/extension/fixtures.js +1 -2
  47. package/dist/adapters/extension/launch.js +4 -5
  48. package/dist/adapters/extension/launched-browser.js +1 -2
  49. package/dist/adapters/extension/network-observer.js +14 -1
  50. package/dist/adapters/extension/overlay.js +1 -1
  51. package/dist/adapters/extension/product-config.js +1 -1
  52. package/dist/adapters/extension/readiness.js +1 -1
  53. package/dist/adapters/extension/reload.js +1 -1
  54. package/dist/adapters/extension/run.js +135 -14
  55. package/dist/adapters/extension/runtime-decision.js +2 -1
  56. package/dist/adapters/extension/runtime.js +9 -5
  57. package/dist/adapters/extension/surface.js +15 -2
  58. package/dist/adapters/extension/verify.js +2 -2
  59. package/dist/adapters/harness-freshness.js +2 -1
  60. package/dist/adapters/mobile/actions.js +4 -1
  61. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  62. package/dist/adapters/mobile/app-identity.js +10 -0
  63. package/dist/adapters/mobile/doctor-checks.js +1 -1
  64. package/dist/adapters/mobile/fixtures.js +1 -1
  65. package/dist/adapters/mobile/launch.js +27 -28
  66. package/dist/adapters/mobile/lifecycle.js +1 -1
  67. package/dist/adapters/mobile/metro-env.js +9 -59
  68. package/dist/adapters/mobile/network-observer.js +68 -0
  69. package/dist/adapters/mobile/performance-observer.js +2 -4
  70. package/dist/adapters/mobile/prepare-target.js +1 -1
  71. package/dist/adapters/mobile/prepare.js +3 -4
  72. package/dist/adapters/mobile/provision.js +1 -1
  73. package/dist/adapters/mobile/readiness.js +1 -1
  74. package/dist/adapters/mobile/recording-target.js +15 -0
  75. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  76. package/dist/adapters/mobile/reload.js +1 -1
  77. package/dist/adapters/mobile/run.js +39 -15
  78. package/dist/adapters/mobile/runtime-decision.js +2 -4
  79. package/dist/adapters/mobile/source-freshness.js +22 -126
  80. package/dist/adapters/mobile/surface.js +26 -3
  81. package/dist/adapters/mobile/ui.js +16 -5
  82. package/dist/adapters/mobile/verify.js +2 -3
  83. package/dist/adapters/performance/cdp-trace.js +1 -1
  84. package/dist/adapters/performance/device-platform.js +1 -1
  85. package/dist/adapters/perps-order-inputs.js +36 -0
  86. package/dist/adapters/proof-failure.js +11 -0
  87. package/dist/adapters/slot-ports.js +1 -1
  88. package/dist/adapters/surface.js +1 -0
  89. package/dist/adapters/terminal/actions.js +4 -1
  90. package/dist/adapters/terminal/console-capture.js +1 -1
  91. package/dist/adapters/terminal/doctor-checks.js +2 -0
  92. package/dist/adapters/terminal/launch.js +4 -1
  93. package/dist/adapters/terminal/overlay.js +8 -1
  94. package/dist/adapters/terminal/run.js +33 -5
  95. package/dist/adapters/terminal/surface.js +9 -1
  96. package/dist/adapters.js +27 -7
  97. package/dist/advisor-policy.js +1 -1
  98. package/dist/checkout-view.js +1 -2
  99. package/dist/cli.js +59 -21
  100. package/dist/command-contract.js +1 -26
  101. package/dist/commands/check.js +33 -10
  102. package/dist/commands/checklist.js +1 -1
  103. package/dist/commands/completion-candidates.js +6 -14
  104. package/dist/commands/config.js +2 -2
  105. package/dist/commands/core-readiness.js +12 -7
  106. package/dist/commands/debug.js +3 -3
  107. package/dist/commands/device-target.js +5 -5
  108. package/dist/commands/doctor.js +2 -14
  109. package/dist/commands/domain.js +2 -2
  110. package/dist/commands/ensure-ready.js +1 -7
  111. package/dist/commands/fixtures.js +5 -6
  112. package/dist/commands/help.js +3 -4
  113. package/dist/commands/launch/device.js +10 -17
  114. package/dist/commands/launch/extension.js +2 -8
  115. package/dist/commands/launch/mobile.js +1 -1
  116. package/dist/commands/logs.js +3 -3
  117. package/dist/commands/mobile-device-view.js +4 -2
  118. package/dist/commands/overlay.js +110 -0
  119. package/dist/commands/pin.js +1 -1
  120. package/dist/commands/pr-body.js +2 -2
  121. package/dist/commands/prepare.js +4 -9
  122. package/dist/commands/provision.js +3 -12
  123. package/dist/commands/recipe-advice.js +1 -2
  124. package/dist/commands/recipe-quality.js +2 -3
  125. package/dist/commands/resolve-extension.js +1 -7
  126. package/dist/commands/review.js +4 -4
  127. package/dist/commands/runtime-decision.js +1 -6
  128. package/dist/commands/runtime-health.js +1 -7
  129. package/dist/commands/runtime-launch.js +1 -13
  130. package/dist/commands/self-test.js +5 -8
  131. package/dist/commands/status-watch.js +1 -2
  132. package/dist/commands/status.js +2 -5
  133. package/dist/commands/task-init.js +2 -3
  134. package/dist/commands/tutorial.js +2 -2
  135. package/dist/commands/update.js +1 -1
  136. package/dist/completions-cache.js +1 -1
  137. package/dist/console-classifier.js +19 -0
  138. package/dist/doctor.js +2 -3
  139. package/dist/engine.js +57 -0
  140. package/dist/funding-execution-context.js +96 -1
  141. package/dist/host.js +32 -0
  142. package/dist/index.js +1 -6
  143. package/dist/live-tsx.js +49 -0
  144. package/dist/mm-harness-cli.js +4 -6
  145. package/dist/paths.js +34 -64
  146. package/dist/performance-observation.js +3 -5
  147. package/dist/progress.js +1 -1
  148. package/dist/recipe-assistance-context.js +6 -9
  149. package/dist/recipe-security.js +2 -59
  150. package/dist/review/knowledge.js +2 -2
  151. package/dist/runner.js +7 -4
  152. package/dist/runtime-context.js +1 -1
  153. package/dist/task-view.js +2 -3
  154. package/docs/CONTRIBUTING.md +5 -3
  155. package/docs/RECIPES.md +31 -3
  156. package/docs/cli-contract.md +2 -2
  157. package/docs/feature-guard.md +90 -0
  158. package/library/README.md +3 -1
  159. package/library/actions/core/perps/_controller.mjs +153 -134
  160. package/library/actions/core/perps/_rewards.mjs +186 -0
  161. package/library/actions/core/perps/close_positions.mjs +1 -1
  162. package/library/actions/core/perps/place_order.mjs +167 -45
  163. package/library/actions/core/perps/quote_fees.mjs +61 -0
  164. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  165. package/library/actions/extension/feature_flags/state.mjs +1 -1
  166. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  167. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  168. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  169. package/library/actions/extension/perps/perps.mjs +320 -3
  170. package/library/actions/extension/perps/place_order.mjs +3 -1
  171. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  172. package/library/actions/extension/platform/cdp.mjs +73 -11
  173. package/library/actions/harness-exports.mjs +1 -6
  174. package/library/actions/mobile/app/network-control.mjs +6 -8
  175. package/library/actions/mobile/perps/perps.mjs +216 -11
  176. package/library/actions/mobile/perps/place_order.mjs +3 -1
  177. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  178. package/library/actions/mobile/platform/bridge.mjs +8 -8
  179. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  180. package/library/actions/mobile/platform/native-session.mjs +3 -4
  181. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  182. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  183. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  184. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  185. package/library/actions/shared/console-findings.mjs +1 -1
  186. package/library/actions/shared/perps/order-policy.mjs +177 -0
  187. package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
  188. package/library/actions/terminal/app/launch.mjs +3 -2
  189. package/library/actions/terminal/platform/hud.mjs +2 -1
  190. package/library/actions/terminal/platform/page.mjs +2 -1
  191. package/library/actions/terminal/platform/runtime.mjs +2 -1
  192. package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
  193. package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
  194. package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
  195. package/library/manifests/core.action-manifest.json +87 -13
  196. package/library/manifests/extension.action-manifest.json +4 -0
  197. package/library/manifests/mobile.action-manifest.json +12 -0
  198. package/package.json +8 -9
  199. package/site/assets/help-recipes.json +1 -0
  200. package/adapters/extension/lib/slot-title.cjs +0 -292
  201. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  202. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  203. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  204. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  205. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  206. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  207. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  208. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  209. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  210. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  211. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  212. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  213. package/adapters/mobile/launch-metro.cjs +0 -77
  214. package/adapters/mobile/metro-config.cjs +0 -98
  215. package/adapters/mobile/metro-log-generation.cjs +0 -106
  216. package/adapters/terminal/lib/origin.mjs +0 -30
  217. package/adapters/terminal/lib/page-script.mjs +0 -246
  218. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  219. package/dist/adapters/mobile/frame-metrics.js +0 -118
  220. package/dist/adapters/mobile/video-recorder.js +0 -289
  221. package/dist/artifact-files.js +0 -92
  222. package/dist/checkout-lock.js +0 -149
  223. package/dist/cli-color.js +0 -98
  224. package/dist/command-journal.js +0 -308
  225. package/dist/commands/call.js +0 -685
  226. package/dist/commands/last.js +0 -60
  227. package/dist/commands/launch/index.js +0 -109
  228. package/dist/commands/list-executables.js +0 -247
  229. package/dist/commands/manifest.js +0 -488
  230. package/dist/commands/parse-args.js +0 -279
  231. package/dist/commands/reload.js +0 -41
  232. package/dist/commands/run-engine.js +0 -1340
  233. package/dist/commands/run-report.js +0 -241
  234. package/dist/commands/run.js +0 -751
  235. package/dist/commands/shared.js +0 -323
  236. package/dist/commands/stop.js +0 -60
  237. package/dist/devices.js +0 -100
  238. package/dist/discovery.js +0 -15
  239. package/dist/execution-provenance.js +0 -332
  240. package/dist/harness.js +0 -451
  241. package/dist/heal-bounds.js +0 -137
  242. package/dist/json-stream.js +0 -60
  243. package/dist/leaf-invoke.js +0 -19
  244. package/dist/library-provenance.js +0 -33
  245. package/dist/live-adapter-contract.js +0 -380
  246. package/dist/metamask-action-validation.js +0 -68
  247. package/dist/network-observation.js +0 -290
  248. package/dist/recording-target.js +0 -120
  249. package/dist/run-diagnostics.js +0 -364
  250. package/dist/run-recording.js +0 -481
  251. package/dist/runtime-proof.js +0 -90
  252. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
@@ -1,135 +1,31 @@
1
- import { execFileSync } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import { recipeRuntimePath } from "../../paths.js";
6
- const SOURCE_PATHS = [
7
- "app",
8
- "locales",
9
- "index.js",
10
- "shim.js",
11
- "shimPerf.js",
12
- "ReactotronConfig.js",
13
- "wdyr.js",
14
- "app.config.js",
15
- "babel.config.js",
16
- "metro.config.js",
17
- "metro.transform.js",
18
- "package.json"
19
- ];
20
- const ENV_INPUTS = [".js.env", ".env", ".env.local"];
1
+ import { recordSourceBaseline, sourceCheck, sourceFingerprint } from "@farmslot/adapter-rn";
2
+ import { recipeRuntimePath } from "@farmslot/recipe-cli/harness";
3
+ const MOBILE_SOURCE_INPUTS = {
4
+ paths: [
5
+ "app",
6
+ "locales",
7
+ "index.js",
8
+ "shim.js",
9
+ "shimPerf.js",
10
+ "ReactotronConfig.js",
11
+ "wdyr.js",
12
+ "app.config.js",
13
+ "babel.config.js",
14
+ "metro.config.js",
15
+ "metro.transform.js",
16
+ "package.json"
17
+ ],
18
+ envFiles: [".js.env", ".env", ".env.local"]
19
+ };
21
20
  const BASELINE_FILE = "mobile-source-baseline.json";
22
21
  function mobileSourceFingerprint(target) {
23
- const hash = createHash("sha256");
24
- const head = tryGit(target, ["rev-parse", "HEAD"]);
25
- if (head === null) {
26
- hash.update("non-git\0");
27
- for (const relative of SOURCE_PATHS) {
28
- hashPath(hash, target, relative);
29
- }
30
- } else {
31
- hash.update(head);
32
- hash.update(git(target, ["diff", "--no-ext-diff", "--binary", "HEAD", "--", ...SOURCE_PATHS]));
33
- const untracked = git(target, [
34
- "ls-files",
35
- "--others",
36
- "--exclude-standard",
37
- "-z",
38
- "--",
39
- ...SOURCE_PATHS
40
- ]).toString("utf8").split("\0").filter(Boolean).sort();
41
- for (const relative of untracked) {
42
- hash.update(`untracked\0${relative}\0`);
43
- hash.update(fs.readFileSync(path.join(target, relative)));
44
- hash.update("\0");
45
- }
46
- }
47
- for (const relative of ENV_INPUTS) {
48
- const absolute = path.join(target, relative);
49
- hash.update(`env\0${relative}\0`);
50
- hash.update(fs.existsSync(absolute) ? fs.readFileSync(absolute) : "<absent>");
51
- hash.update("\0");
52
- }
53
- return hash.digest("hex");
22
+ return sourceFingerprint(target, MOBILE_SOURCE_INPUTS);
54
23
  }
55
24
  function mobileSourceCheck(target) {
56
- const fingerprint = mobileSourceFingerprint(target);
57
- const baseline = readBaseline(target);
58
- return {
59
- fingerprint,
60
- status: baseline === null ? "missing" : baseline.fingerprint === fingerprint ? "current" : "changed"
61
- };
25
+ return sourceCheck(target, MOBILE_SOURCE_INPUTS, recipeRuntimePath(target, BASELINE_FILE));
62
26
  }
63
27
  function recordMobileSourceBaseline(target, expectedFingerprint) {
64
- if (mobileSourceFingerprint(target) !== expectedFingerprint) return false;
65
- const marker = recipeRuntimePath(target, BASELINE_FILE);
66
- const temporary = `${marker}.${process.pid}.${Date.now()}.tmp`;
67
- const baseline = {
68
- schemaVersion: 1,
69
- fingerprint: expectedFingerprint,
70
- recordedAt: (/* @__PURE__ */ new Date()).toISOString()
71
- };
72
- fs.mkdirSync(path.dirname(marker), { recursive: true });
73
- try {
74
- fs.writeFileSync(temporary, `${JSON.stringify(baseline, null, 2)}
75
- `, { mode: 384 });
76
- if (mobileSourceFingerprint(target) !== expectedFingerprint) return false;
77
- fs.renameSync(temporary, marker);
78
- if (mobileSourceFingerprint(target) !== expectedFingerprint) {
79
- if (readBaseline(target)?.fingerprint === expectedFingerprint) fs.rmSync(marker, { force: true });
80
- return false;
81
- }
82
- } finally {
83
- fs.rmSync(temporary, { force: true });
84
- }
85
- return true;
86
- }
87
- function git(target, args) {
88
- return execFileSync("git", args, {
89
- cwd: target,
90
- encoding: "buffer",
91
- maxBuffer: 128 * 1024 * 1024,
92
- stdio: ["ignore", "pipe", "ignore"]
93
- });
94
- }
95
- function tryGit(target, args) {
96
- try {
97
- return git(target, args);
98
- } catch {
99
- return null;
100
- }
101
- }
102
- function hashPath(hash, root, relative) {
103
- const absolute = path.join(root, relative);
104
- if (!fs.existsSync(absolute)) {
105
- hash.update(`absent\0${relative}\0`);
106
- return;
107
- }
108
- const stat = fs.lstatSync(absolute);
109
- if (stat.isDirectory()) {
110
- hash.update(`directory\0${relative}\0`);
111
- for (const child of fs.readdirSync(absolute).sort()) {
112
- hashPath(hash, root, path.join(relative, child));
113
- }
114
- return;
115
- }
116
- if (stat.isSymbolicLink()) {
117
- hash.update(`symlink\0${relative}\0${fs.readlinkSync(absolute)}\0`);
118
- return;
119
- }
120
- hash.update(`file\0${relative}\0`);
121
- hash.update(fs.readFileSync(absolute));
122
- hash.update("\0");
123
- }
124
- function readBaseline(target) {
125
- try {
126
- const parsed = JSON.parse(
127
- fs.readFileSync(recipeRuntimePath(target, BASELINE_FILE), "utf8")
128
- );
129
- return parsed.schemaVersion === 1 && typeof parsed.fingerprint === "string" ? parsed : null;
130
- } catch {
131
- return null;
132
- }
28
+ return recordSourceBaseline(target, MOBILE_SOURCE_INPUTS, recipeRuntimePath(target, BASELINE_FILE), expectedFingerprint);
133
29
  }
134
30
  export {
135
31
  mobileSourceCheck,
@@ -1,19 +1,19 @@
1
+ import { recipeRuntimePath, shellQuote, writeInteractiveProgress } from "@farmslot/recipe-cli/harness";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import path from "node:path";
3
4
  import { defineAdapter } from "@farmslot/adapter-sdk";
4
- import { recipeRuntimePath, runnerDir } from "../../paths.js";
5
- import { shellQuote } from "../../commands/parse-args.js";
5
+ import { runnerDir } from "../../paths.js";
6
6
  import { resolveMobileSlotPorts } from "../slot-ports.js";
7
7
  import { mobileRuntimeStatus } from "./prepare.js";
8
8
  import { hasRunwayProvisionBaseline, provisionRunwayMobile } from "./provision.js";
9
9
  import { adapterRegistry } from "../registry.js";
10
- import { writeInteractiveProgress } from "../../commands/shared.js";
11
10
  import { mobileActions } from "./actions.js";
12
11
  import { mobileHarness, mobilePrepare, mobileRuntimeContext } from "./overlay.js";
13
12
  import { mobileReadiness, mobileRuntimeDecision } from "./readiness.js";
14
13
  import { clearMobileReleaseArtifactState } from "./release-artifact-state.js";
15
14
  import { mobileDiagnostics, mobileObservation, mobileRecording, mobileRun } from "./run.js";
16
15
  import { mobileSourceFingerprint } from "./source-freshness.js";
16
+ import { checkoutHas, checkoutHasDir } from "../checkout-files.js";
17
17
  function mobileRuntimeNextAction(reasonCode, target, platform) {
18
18
  const resolved = shellQuote(path.resolve(target));
19
19
  return reasonCode === "deps-missing" || reasonCode === "deps-partial" || reasonCode === "deps-stale" ? `cd ${resolved} && yarn setup` : `mm-harness launch ${platform} --adapter mobile --target ${resolved}`;
@@ -53,6 +53,9 @@ const mobileSurface = defineAdapter({
53
53
  devServer: {
54
54
  label: "metro",
55
55
  describe: () => "Metro dev server",
56
+ // Metro's leaves read the port as METRO_PORT; run and call take it as --metro-port.
57
+ portEnv: ["METRO_PORT"],
58
+ portFlags: ["metroPort"],
56
59
  stop(target) {
57
60
  const leaf = path.join(runnerDir, "adapters", "mobile", "stop-metro.sh");
58
61
  const args = ["--target", target];
@@ -70,6 +73,26 @@ const mobileSurface = defineAdapter({
70
73
  appLogSource(target) {
71
74
  return { label: "app", path: recipeRuntimePath(target, "app-console.log") };
72
75
  },
76
+ detect: {
77
+ remote: (url) => url.includes("metamask-mobile"),
78
+ files: (target) => checkoutHas(target, "scripts/skills-sync.mts") || checkoutHasDir(target, "ios") && checkoutHasDir(target, "android") && checkoutHasDir(target, "app/core")
79
+ },
80
+ // `launch ios|android`, and --platform ios|android, select Mobile.
81
+ targets: ["ios", "android"],
82
+ flags: {
83
+ launch: ["build", "watch", "verify", "runway", "clearMetro"],
84
+ commands: ["clearMetro", "resolveOnly"]
85
+ },
86
+ failurePatterns: {
87
+ captureProtected: {
88
+ pattern: /SCREENSHOT_PROTECTED|screenshot is protected by FLAG_SECURE/iu,
89
+ message: "the focused Android window intentionally blocks screenshots; a black capture cannot be used as evidence.",
90
+ userAction: "unlock or navigate to a non-protected screen, then rerun the screenshot action"
91
+ },
92
+ transportFirst: /CDP_TIMEOUT|Mobile CDP bridge command timed out|NO_TARGET|WS_CLOSED/iu,
93
+ walletState: /fixture|keyring|not seeded|\bsrp\b|password|onboard/iu,
94
+ transport: /metro|bridge|bundle|packager|dev client/iu
95
+ },
73
96
  hints: {
74
97
  launch: "mm-harness launch ios",
75
98
  relaunch: "mm-harness launch ios",
@@ -3,9 +3,13 @@ import { promisify } from "node:util";
3
3
  import { bridgeCommand, evalAsync, evalSync, MOBILE_BRIDGE_ERROR_CODES, selectBridgeStatusEntry, simulatorScreenshot } from "../../../library/actions/mobile/platform/bridge.mjs";
4
4
  import { observeNativeUi } from "../../../library/actions/mobile/platform/observe-ui.mjs";
5
5
  import { nativeAgentDeviceStateDir } from "../../../library/actions/mobile/platform/native-session-name.mjs";
6
- import { resolveMobileToolPath } from "../../../library/actions/mobile/platform/tool-paths.mjs";
6
+ import { resolveMobileToolPath } from "@farmslot/adapter-rn";
7
7
  import { isRecord, traceText } from "../../action-values.js";
8
- import { runLiveAdapterScript } from "../../live-adapter-contract.js";
8
+ import { runLiveAdapterScript } from "@farmslot/recipe-cli/harness";
9
+ import { trustedMutationContextExtras } from "../../funding-execution-context.js";
10
+ import { LIBRARY_ACTION_NAMESPACE } from "../../paths.js";
11
+ import { liveTsxCandidates } from "../../live-tsx.js";
12
+ import { mobileAppIdentityEnv } from "./app-identity.js";
9
13
  const execFileAsync = promisify(execFile);
10
14
  function sleep(ms) {
11
15
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -213,7 +217,10 @@ async function sendMobileBridgeCommand(command, context) {
213
217
  if (!handler) {
214
218
  throw new Error(`React Native bridge command ${command.command} is not implemented by the MetaMask runner transport.`);
215
219
  }
216
- return handler(command.payload, context);
220
+ return handler(command.payload, {
221
+ ...context,
222
+ env: { ...context.env, ...mobileAppIdentityEnv(context.env) }
223
+ });
217
224
  }
218
225
  function mobileUiInput(context, command, payload) {
219
226
  return uiInputFor(context, { action: `ui.${command}`, node: payload });
@@ -235,8 +242,11 @@ async function handleMobileNavigate(payload, context) {
235
242
  const live = await runLiveAdapterScript({
236
243
  platform: "mobile",
237
244
  action: "ui.navigate",
245
+ namespace: LIBRARY_ACTION_NAMESPACE,
238
246
  node: payload,
239
- context
247
+ context,
248
+ contextExtras: trustedMutationContextExtras(context),
249
+ tsxCandidates: liveTsxCandidates()
240
250
  });
241
251
  if (!live) throw new Error("ui.navigate requires library/actions/mobile/ui/navigate.mjs.");
242
252
  return isRecord(live.result) ? { ...live.result, liveAdapter: live.script } : live.result;
@@ -586,7 +596,8 @@ async function executeNativeProviderAction(action, node, context, createTranspor
586
596
  if (platform === "android" && action === "ui.swipe" && isCoordinateTarget(node.target)) {
587
597
  return executeAndroidCoordinateSwipe(node, String(device));
588
598
  }
589
- const app = platform === "android" ? context.env.ANDROID_PACKAGE_ID ?? process.env.ANDROID_PACKAGE_ID ?? "io.metamask" : context.env.IOS_BUNDLE_ID ?? process.env.IOS_BUNDLE_ID ?? "io.metamask.MetaMask";
599
+ const identity = mobileAppIdentityEnv(context.env);
600
+ const app = platform === "android" ? identity.ANDROID_PACKAGE_ID : identity.IOS_BUNDLE_ID;
590
601
  const session = (platform === "ios" ? `mm-harness-${process.pid}-${device}` : `mm-harness-${process.pid}-${context.nodeId}`).replace(/[^a-zA-Z0-9._-]/gu, "-");
591
602
  const stateDir = nativeAgentDeviceStateDir(context.env ?? {}, context.projectRoot, String(device));
592
603
  let transport;
@@ -1,10 +1,9 @@
1
+ import { recipeHarnessPath, recipeHarnessRoot, recipeRuntimeDir, spawnScriptStreaming, readRuntimeContextField } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import crypto from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import path from "node:path";
5
- import { spawnScriptStreaming } from "../../commands/shared.js";
6
- import { readRuntimeContextField } from "../../harness.js";
7
- import { recipeHarnessPath, recipeHarnessRoot, recipeRuntimeDir, runnerDir } from "../../paths.js";
6
+ import { runnerDir } from "../../paths.js";
8
7
  const USAGE = "Usage: mm-harness verify --adapter mobile -- [--target <metamask-mobile>] [--artifacts-dir <dir>] [--static-only] [--platform ios|android] [--preflight-mode fast|auto|default|rebuild-native|clean] [--no-auto-start]";
9
8
  const ENV_FILES = [".js.env", ".env", ".env.local"];
10
9
  const PREFLIGHT_MODES = ["fast", "auto", "default", "rebuild-native", "clean"];
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  summarizeFrames
3
- } from "../mobile/frame-metrics.js";
3
+ } from "@farmslot/adapter-rn";
4
4
  import { withTimeout } from "../../async.js";
5
5
  import {
6
6
  summarizeJavaScriptTasks
@@ -1,6 +1,6 @@
1
1
  function devicePlatformFromEnv(env) {
2
2
  const explicit = String(
3
- env.MM_HARNESS_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
3
+ env.RECIPE_RN_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
4
4
  ).toLowerCase();
5
5
  if (explicit === "android" || explicit === "ios") return explicit;
6
6
  return env.ADB_SERIAL || env.ANDROID_SERIAL || env.ANDROID_DEVICE ? "android" : "ios";
@@ -0,0 +1,36 @@
1
+ function perpsOrderInputFindings(nodeId, node, { ensureChecksOnly }) {
2
+ const action = node.action;
3
+ const state = typeof node.state === "string" ? node.state.toLowerCase() : void 0;
4
+ const createsOrder = action === "metamask.perps.place_order" || !ensureChecksOnly && (action === "metamask.perps.ensure_positions" || action === "metamask.perps.ensure_orders") && (state === "open" || state === "present");
5
+ if (!createsOrder) return [];
6
+ const findings = [];
7
+ const requireOneOf = (names, message) => {
8
+ const present = names.some((name) => {
9
+ const value = node[name];
10
+ return value !== void 0 && value !== null && value !== "";
11
+ });
12
+ if (present) return;
13
+ findings.push({
14
+ severity: "error",
15
+ code: "recipe.missing_param",
16
+ path: `workflow.nodes.${nodeId}.${names[0]}`,
17
+ message
18
+ });
19
+ };
20
+ requireOneOf(
21
+ ["market", "symbol"],
22
+ `${action} requires market=<symbol> (or symbol=<symbol>) before it may create an order`
23
+ );
24
+ requireOneOf(
25
+ ["side"],
26
+ `${action} requires side=long or side=short before it may create an order`
27
+ );
28
+ requireOneOf(
29
+ ["amount", "notional"],
30
+ `${action} requires amount=<usd> or notional=<usd> before it may create an order`
31
+ );
32
+ return findings;
33
+ }
34
+ export {
35
+ perpsOrderInputFindings
36
+ };
@@ -0,0 +1,11 @@
1
+ function emitProofFailure(json, failure) {
2
+ if (json) {
3
+ console.log(JSON.stringify({ schemaVersion: 1, status: "fail", ...failure }, null, 2));
4
+ return;
5
+ }
6
+ console.error(`\u2717 ${failure.error.message}`);
7
+ console.error(` Next: ${failure.error.userAction}`);
8
+ }
9
+ export {
10
+ emitProofFailure
11
+ };
@@ -1,8 +1,8 @@
1
+ import { recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import os from "node:os";
4
5
  import path from "node:path";
5
- import { recipeRuntimeDir } from "../paths.js";
6
6
  import {
7
7
  formatExtensionIdentityPorts,
8
8
  resolveDefaultExtensionPorts,
@@ -1,3 +1,4 @@
1
+ import "../host.js";
1
2
  import { adapterRegistry } from "./registry.js";
2
3
  import "./mobile/surface.js";
3
4
  import "./extension/surface.js";
@@ -1,4 +1,5 @@
1
1
  import { manifestPath } from "../../paths.js";
2
+ import { perpsOrderInputFindings } from "../perps-order-inputs.js";
2
3
  const terminalActions = {
3
4
  manifestPath: () => manifestPath("terminal"),
4
5
  semantic: [
@@ -36,7 +37,9 @@ const terminalActions = {
36
37
  }
37
38
  return standard();
38
39
  }
39
- })
40
+ }),
41
+ // ensure_positions and ensure_orders with an open or present state place an order.
42
+ inputFindings: (nodeId, node) => perpsOrderInputFindings(nodeId, node, { ensureChecksOnly: false })
40
43
  };
41
44
  export {
42
45
  terminalActions
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { recipeRuntimePath } from "@farmslot/recipe-cli/harness";
3
4
  import { verifyCapture } from "../../../library/actions/shared/console-collector.mjs";
4
- import { recipeRuntimePath } from "../../paths.js";
5
5
  import { ensureConsoleCollector } from "../console-capture.js";
6
6
  const TERMINAL_CONSOLE_FILES = Object.freeze({
7
7
  app: "app-console.log",
@@ -2,7 +2,9 @@ import { terminalAppPort, terminalCdpPort } from "./surface.js";
2
2
  async function terminalDoctorChecks(target) {
3
3
  const { terminalReadiness } = await import("../../../adapters/terminal/lib/readiness.mjs");
4
4
  const { resolveBrowser } = await import("../../../adapters/terminal/launch.mjs");
5
+ const { policy } = await import("../../../adapters/terminal/policy.mjs");
5
6
  const report = await terminalReadiness({
7
+ policy,
6
8
  target,
7
9
  appPort: terminalAppPort(),
8
10
  cdpPort: terminalCdpPort(),
@@ -1,6 +1,6 @@
1
+ import { flag, str } from "@farmslot/recipe-cli/harness";
1
2
  import { execFileSync } from "node:child_process";
2
3
  import path from "node:path";
3
- import { flag, str } from "../../commands/shared.js";
4
4
  import { runnerDir } from "../../paths.js";
5
5
  const TERMINAL_ONLY_LAUNCH_OPTIONS = Object.freeze({
6
6
  signer: "--signer",
@@ -46,6 +46,9 @@ async function launchTerminal({ options, target, jsonOutput, stream, usage }) {
46
46
  cdpPort,
47
47
  "--app-port",
48
48
  appPort,
49
+ // The Terminal's venue policy (Hyperliquid hosts, refusal, start page).
50
+ "--policy-module",
51
+ path.join(runnerDir, "adapters", "terminal", "policy.mjs"),
49
52
  "--signer",
50
53
  str(options, "signer") ?? process.env.TERMINAL_SIGNER ?? "extension",
51
54
  "--account",
@@ -3,7 +3,14 @@ import { runnerDir } from "../../paths.js";
3
3
  const terminalHarness = {
4
4
  install: { entry: "adapters/terminal/inject.mjs", fallback: "adapters/terminal/inject.mjs", node: true },
5
5
  cleanup: { entry: "adapters/terminal/cleanup.mjs", fallback: "adapters/terminal/cleanup.mjs", node: true },
6
- verify: () => ({ command: process.execPath, prefixArgs: [path.join(runnerDir, "adapters", "terminal", "verify.mjs")] }),
6
+ verify: () => ({
7
+ command: process.execPath,
8
+ prefixArgs: [
9
+ path.join(runnerDir, "adapters", "terminal", "verify.mjs"),
10
+ "--policy-module",
11
+ path.join(runnerDir, "adapters", "terminal", "policy.mjs")
12
+ ]
13
+ }),
7
14
  restart: "mm-harness launch"
8
15
  };
9
16
  const terminalRuntimeContext = {
@@ -1,3 +1,5 @@
1
+ import { isRecord } from "@farmslot/recipe-cli/harness";
2
+ import { verifyOwnedCollectorControl } from "../console-capture.js";
1
3
  import { devicePlatformFromEnv } from "../performance/device-platform.js";
2
4
  import {
3
5
  ensureTerminalConsoleCapture,
@@ -11,17 +13,43 @@ const terminalDiagnostics = {
11
13
  start: (projectRoot) => ensureTerminalConsoleCapture(projectRoot),
12
14
  files: (projectRoot) => terminalCaptureFiles(projectRoot),
13
15
  cdpPort: () => terminalCdpPort(),
14
- verifyControl: (capture) => verifyTerminalConsoleControl(capture)
16
+ verifyControl: (capture) => verifyOwnedCollectorControl(capture, () => verifyTerminalConsoleControl(capture))
15
17
  },
16
- walletLog: (projectRoot) => terminalRuntimeFile(projectRoot, "wallet-requests.jsonl")
18
+ requestLog: {
19
+ path: (projectRoot) => terminalRuntimeFile(projectRoot, "wallet-requests.jsonl"),
20
+ findings: (lines) => walletRequestFindings(lines)
21
+ }
17
22
  };
23
+ function walletRequestFindings(lines) {
24
+ const findings = [];
25
+ for (const line of lines) {
26
+ let entry;
27
+ try {
28
+ entry = JSON.parse(line);
29
+ } catch {
30
+ continue;
31
+ }
32
+ if (!isRecord(entry)) continue;
33
+ let text = null;
34
+ if (entry.kind === "blocked-mainnet" && entry.probe !== true) text = `blocked-mainnet: ${String(entry.transport ?? "request")} to ${String(entry.url ?? "?")}`;
35
+ else if (entry.kind === "refused-mainnet") text = `refused-mainnet: ${String(entry.method ?? "?")} (${String(entry.reason ?? "?")})`;
36
+ else if (entry.kind === "unattributed") text = `unattributed wallet ${entry.binding === "request" ? "request" : "log entry"}${typeof entry.method === "string" ? ` (${entry.method})` : ""}`;
37
+ if (text) findings.push({ level: "error", source: "wallet", text });
38
+ }
39
+ return findings;
40
+ }
18
41
  const terminalObservation = {
19
42
  network: {
20
- backend: async (_target, env, artifactsDir) => (await import("../../network-observation.js")).createExtensionNetworkBackend(env, artifactsDir)
43
+ backend: async (_target, env, artifactsDir) => (await import("../extension/network-observer.js")).createExtensionNetworkBackend(env, artifactsDir)
21
44
  },
22
45
  performance: {
23
- backend: async (target, env, ports) => (await import("../../performance-observation.js")).createExtensionPerformanceObserver(target, env, ports),
24
- platform: (env) => devicePlatformFromEnv(env)
46
+ start: async (context) => {
47
+ const observation = await import("../../performance-observation.js");
48
+ return observation.startRunPerformanceObservation({
49
+ backend: (target, env, ports) => observation.createExtensionPerformanceObserver(target, env, ports),
50
+ platform: (env) => devicePlatformFromEnv(env)
51
+ }, context);
52
+ }
25
53
  }
26
54
  };
27
55
  export {
@@ -1,6 +1,6 @@
1
+ import { shellQuote } from "@farmslot/recipe-cli/harness";
1
2
  import path from "node:path";
2
3
  import { defineAdapter } from "@farmslot/adapter-sdk";
3
- import { shellQuote } from "../../commands/parse-args.js";
4
4
  import fs from "node:fs";
5
5
  import { TERMINAL_CONSOLE_FILES, terminalRuntimeFile } from "./console-capture.js";
6
6
  import { stopTerminalBrowserSync } from "./stop.js";
@@ -9,6 +9,7 @@ import { terminalActions } from "./actions.js";
9
9
  import { terminalHarness, terminalRuntimeContext } from "./overlay.js";
10
10
  import { terminalReadiness } from "./readiness.js";
11
11
  import { terminalDiagnostics, terminalObservation } from "./run.js";
12
+ import { checkoutHas, checkoutHasDir } from "../checkout-files.js";
12
13
  function portFromEnv(...names) {
13
14
  for (const name of names) {
14
15
  const raw = process.env[name];
@@ -38,7 +39,9 @@ const terminalSurface = defineAdapter({
38
39
  },
39
40
  async runtimeStatus(target) {
40
41
  const { terminalReadiness: terminalReadiness2 } = await import("../../../adapters/terminal/lib/readiness.mjs");
42
+ const { policy } = await import("../../../adapters/terminal/policy.mjs");
41
43
  const report = await terminalReadiness2({
44
+ policy,
42
45
  target: path.resolve(target),
43
46
  appPort: terminalAppPort(),
44
47
  cdpPort: terminalCdpPort(),
@@ -98,6 +101,11 @@ const terminalSurface = defineAdapter({
98
101
  appLogSource(target) {
99
102
  return { label: "app-console", path: terminalRuntimeFile(target, TERMINAL_CONSOLE_FILES.app) };
100
103
  },
104
+ detect: {
105
+ remote: (url) => url.includes("va-mmcx-terminal"),
106
+ files: (target) => checkoutHasDir(target, "src/features/perpetuals") && ["next.config.ts", "next.config.mjs", "next.config.js"].some((file) => checkoutHas(target, file))
107
+ },
108
+ flags: { launch: ["freshProfile", "headless", "headful"] },
101
109
  hints: {
102
110
  launch: "mm-harness launch --adapter terminal --cdp-port <port> --watcher-port <app port>",
103
111
  relaunch: "mm-harness launch --adapter terminal --fresh-profile",
package/dist/adapters.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import http from "node:http";
2
2
  import { createHash } from "node:crypto";
3
3
  import { compatibilityMode, fixtureSummary, repoShape } from "./doctor.js";
4
- import {
5
- runLiveAdapterScript
6
- } from "./live-adapter-contract.js";
4
+ import { liveAdapterProcessTimeoutMs, runLiveAdapterScript, runNetworkCaptureAction } from "@farmslot/recipe-cli/harness";
5
+ import { trustedMutationContextExtras } from "./funding-execution-context.js";
6
+ import { LIBRARY_ACTION_NAMESPACE } from "./paths.js";
7
+ import { liveTsxCandidates } from "./live-tsx.js";
7
8
  import { resolveWalletImportCredentials, validateWalletImportOptions } from "../library/actions/shared/wallet/import-source.mjs";
8
9
  import { isRecord, traceText } from "./action-values.js";
9
10
  import { getAdapterSurface } from "./adapters/surface.js";
10
- import { handleRunNetworkAction } from "./network-observation.js";
11
11
  import { handleRunPerformanceAction } from "./performance-observation.js";
12
12
  function simpleAdapter(action, executor) {
13
13
  return {
@@ -99,6 +99,12 @@ function liveRuntimeConfigured(platform, node) {
99
99
  if (!getAdapterSurface(platform).actions.liveNeedsCdpPort) return true;
100
100
  return Boolean(node.cdp_port ?? process.env.CDP_PORT ?? process.env.RECIPE_CDP_PORT);
101
101
  }
102
+ const IMMEDIATE_CLOSE_ALLOWANCE_MS = 18e4;
103
+ function withImmediateCloseBudget(action, node) {
104
+ if (action !== `${LIBRARY_ACTION_NAMESPACE}.perps.place_order`) return node;
105
+ if (node.close_immediately !== true && node.closeImmediately !== true) return node;
106
+ return { ...node, live_adapter_timeout_ms: liveAdapterProcessTimeoutMs(node) + IMMEDIATE_CLOSE_ALLOWANCE_MS };
107
+ }
102
108
  async function runLiveFirst(platform, action, node, context, forceLive = false, preparedLiveAdapters) {
103
109
  if (node.allow_static_placeholder === true) {
104
110
  throw new Error(
@@ -109,9 +115,12 @@ async function runLiveFirst(platform, action, node, context, forceLive = false,
109
115
  const live = await runLiveAdapterScript({
110
116
  platform,
111
117
  action,
112
- node,
118
+ namespace: LIBRARY_ACTION_NAMESPACE,
119
+ node: withImmediateCloseBudget(action, node),
113
120
  context,
114
- prepared: preparedLiveAdapters?.get(action)
121
+ prepared: preparedLiveAdapters?.get(action),
122
+ contextExtras: trustedMutationContextExtras(context),
123
+ tsxCandidates: liveTsxCandidates()
115
124
  });
116
125
  if (!live) return null;
117
126
  const liveResult = isRecord(live.result) ? live.result : { result: live.result };
@@ -127,6 +136,15 @@ function liveAdapterPathHint(platform, action) {
127
136
  }
128
137
  return `library/actions/${platform}/${action.replaceAll(".", "/")}.mjs`;
129
138
  }
139
+ async function handleRunNetworkAction(platform, action, node, context) {
140
+ if (!getAdapterSurface(platform).observation?.network?.actions) return null;
141
+ if (action === "app.network_assert") {
142
+ const assertion = await import("../library/actions/shared/app/network-assert.mjs");
143
+ return { output: await assertion.assertNetwork({ action, node, context }) };
144
+ }
145
+ if (action !== "app.network_capture") return null;
146
+ return runNetworkCaptureAction(platform, node, context);
147
+ }
130
148
  async function semanticResult(platform, action, node, context, forceLive = false, preparedLiveAdapters) {
131
149
  const performanceAction = await handleRunPerformanceAction(
132
150
  platform,
@@ -364,7 +382,9 @@ function createMetaMaskAdapters(adapter, declaredCustomActions = [], preparedLiv
364
382
  return adapters;
365
383
  }
366
384
  export {
385
+ IMMEDIATE_CLOSE_ALLOWANCE_MS,
367
386
  createMetaMaskAdapters,
368
387
  createMetaMaskUiTransport,
369
- normalizeUiWaitNode
388
+ normalizeUiWaitNode,
389
+ withImmediateCloseBudget
370
390
  };
@@ -1,5 +1,5 @@
1
+ import { optionString, usageError } from "@farmslot/recipe-cli/harness";
1
2
  import { assessmentProvider } from "./assessment-providers.js";
2
- import { optionString, usageError } from "./commands/parse-args.js";
3
3
  import { configPath, parseAdvisorSettings, readConfig } from "./review/knowledge.js";
4
4
  function resolveAdvisorPolicy(options, env = process.env, config) {
5
5
  const saved = (config ?? readConfig(configPath(env))).advisor ?? {};
@@ -1,8 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { color } from "./cli-color.js";
3
+ import { color, recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
4
4
  import { renderRuntimeContext, runtimeContextSummary } from "./doctor.js";
5
- import { recipeRuntimeDir } from "./paths.js";
6
5
  import {
7
6
  configPath,
8
7
  declaredLibraryPathEnv,