@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
@@ -0,0 +1,19 @@
1
+ import {
2
+ allowlistMatch,
3
+ classifyConsoleRecord,
4
+ consoleRecords,
5
+ libraryRootsFromEnv,
6
+ loadConsoleAllowlist,
7
+ messageSignature
8
+ } from "../library/actions/shared/console-findings.mjs";
9
+ const consoleClassifier = {
10
+ records: (text) => consoleRecords(text),
11
+ classify: (record) => classifyConsoleRecord(record),
12
+ signature: (message) => messageSignature(message),
13
+ loadAllowlist: (libraryRoots) => loadConsoleAllowlist([...libraryRoots]),
14
+ match: (event, allowlist) => allowlistMatch(event, allowlist),
15
+ libraryRoots: () => libraryRootsFromEnv()
16
+ };
17
+ export {
18
+ consoleClassifier
19
+ };
package/dist/doctor.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { color } from "./cli-color.js";
3
+ import { color, harnessExecutable, recipeHarnessRoot, recipeRuntimeDir, readRuntimeContextField, resolveRuntimeContextPath } from "@farmslot/recipe-cli/harness";
4
4
  import { normalizeFeatureFlagRecord } from "./feature-flags.js";
5
5
  import { getAdapterSurface } from "./adapters/surface.js";
6
- import { readRuntimeContextField, resolveRuntimeContextPath } from "./harness.js";
7
6
  import { isPin } from "./harness-pin.js";
8
- import { harnessExecutable, readJson, recipeHarnessRoot, recipeRuntimeDir, runnerDir } from "./paths.js";
7
+ import { readJson, runnerDir } from "./paths.js";
9
8
  function requiredDoctorCheckSummary(checks) {
10
9
  const required = checks.filter((check) => check.required);
11
10
  const failed = required.filter((check) => check.status === "fail").map((check) => check.id);
package/dist/engine.js ADDED
@@ -0,0 +1,57 @@
1
+ import path from "node:path";
2
+ import { resolveAdapter } from "@farmslot/recipe-cli/harness";
3
+ import { getAdapterSurface } from "./adapters/surface.js";
4
+ import { consoleClassifier } from "./console-classifier.js";
5
+ import {
6
+ authorizeTrustedMutationPlan,
7
+ fixturePolicyMutationBase,
8
+ fundingOptionsFromCli,
9
+ isFixturePolicyBase,
10
+ loadTrustedMutationBase
11
+ } from "./funding-execution-context.js";
12
+ import { resolveActionManifest, validateManifest } from "./manifest.js";
13
+ import { LIBRARY_ACTION_NAMESPACE, runnerDir } from "./paths.js";
14
+ import { metaMaskActionExecutionCapabilities } from "./recipe-security.js";
15
+ const metaMaskEngine = {
16
+ bundledLibrary: {
17
+ name: "metamask",
18
+ root: path.resolve(runnerDir, "library"),
19
+ actionNamespace: LIBRARY_ACTION_NAMESPACE
20
+ },
21
+ resolveActionManifest,
22
+ validateManifest,
23
+ actionCapabilities: metaMaskActionExecutionCapabilities,
24
+ createRunner: async (adapter, manifest, { trustedMutation, ...options }) => {
25
+ const { createMetaMaskRunner } = await import("./runner.js");
26
+ return trustedMutation === NO_PLAN_MUTATION ? createMetaMaskRunner(adapter, manifest, { ...options, suppressLibraryResolutionLogs: true }) : createMetaMaskRunner(adapter, manifest, { ...options, trustedMutation });
27
+ },
28
+ // Funding files from the run's command line, bound to the run's plan. Without
29
+ // them, a platform whose actions take a trusted mutation (the Extension) runs a
30
+ // plan with external mutations in the fixture-policy lane, where each action
31
+ // applies the shared order policy; any other plan runs without one.
32
+ trustedMutation: {
33
+ load: async ({ cli, ...run }) => await loadTrustedMutationBase({ ...fundingOptionsFromCli(cli), ...run }) ?? (takesTrustedMutation(cli) ? fixturePolicyMutationBase({ artifactsDir: run.artifactsDir, rootRecipe: run.rootRecipe }) : void 0),
34
+ authorize: async (base, plan) => {
35
+ if (!base || base === NO_PLAN_MUTATION) return void 0;
36
+ if (isFixturePolicyBase(base) && !plan.nodes.some((node) => node.capabilities.includes("external-mutation"))) {
37
+ return NO_PLAN_MUTATION;
38
+ }
39
+ return authorizeTrustedMutationPlan(base, plan);
40
+ }
41
+ },
42
+ console: consoleClassifier,
43
+ runnerIncludes: ["src", "adapters", "library", "bin", "dist", "package.json"]
44
+ };
45
+ const NO_PLAN_MUTATION = /* @__PURE__ */ Symbol("no plan mutation");
46
+ function takesTrustedMutation(cli) {
47
+ let adapter;
48
+ try {
49
+ ({ adapter } = resolveAdapter(cli));
50
+ } catch {
51
+ return false;
52
+ }
53
+ return getAdapterSurface(adapter).actions.trustedMutation === true;
54
+ }
55
+ export {
56
+ metaMaskEngine
57
+ };
@@ -1,10 +1,10 @@
1
+ import { optionString, recipeRuntimeDir } from "@farmslot/recipe-cli/harness";
1
2
  import { createHash } from "node:crypto";
2
3
  import { constants as fsConstants } from "node:fs";
3
4
  import { chmod, lstat, mkdir, open, readFile, readdir, realpath } from "node:fs/promises";
4
5
  import os from "node:os";
5
6
  import path from "node:path";
6
7
  import { digestRecipeDocument } from "@farmslot/protocol";
7
- import { recipeRuntimeDir } from "./paths.js";
8
8
  const SHA256 = /^[a-f0-9]{64}$/u;
9
9
  const INTEGER = /^(0|[1-9][0-9]{0,39})$/u;
10
10
  const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
@@ -35,6 +35,7 @@ const CLAIM_DIFF_FIELDS = [
35
35
  "operationCounts"
36
36
  ];
37
37
  const trustedActionContexts = /* @__PURE__ */ new WeakSet();
38
+ const fixturePolicySources = /* @__PURE__ */ new WeakMap();
38
39
  const fundingSources = /* @__PURE__ */ new WeakMap();
39
40
  const CURRENT_FUNDING_REGISTRY_SHA256 = "d4416e0e8a812a0e2f1798ae6f901a5820f7ee0b9783cfa5d515d0c7863ea80e";
40
41
  const PREVIOUS_FUNDING_REGISTRY_SHA256 = "7e79ee2a5f7bdde0a5548208cb2eab55881575cc7d826ee5f7acdbf15d49609c";
@@ -141,7 +142,70 @@ async function loadTrustedMutationBase(options) {
141
142
  });
142
143
  return trusted;
143
144
  }
145
+ const FIXTURE_POLICY_LANE = "fixture-policy";
146
+ const fixturePolicyMarker = (field) => sha256(`${FIXTURE_POLICY_LANE}:${field}`);
147
+ function fixturePolicyMutationBase(options) {
148
+ if (!path.isAbsolute(options.artifactsDir)) {
149
+ throw new Error("Runner artifactsDir must be absolute before a mutation context is created.");
150
+ }
151
+ const rootRecipeSha256 = normalizeRecipeDigest(digestRecipeDocument(options.rootRecipe));
152
+ const base = deepFreeze({
153
+ runId: sha256(canonical({ lane: FIXTURE_POLICY_LANE, artifactsDir: options.artifactsDir, rootRecipeSha256 })),
154
+ rootRecipeSha256,
155
+ requestSha256: fixturePolicyMarker("request"),
156
+ preflightSha256: fixturePolicyMarker("preflight"),
157
+ releaseProofSha256: fixturePolicyMarker("release-proof"),
158
+ balanceProofSha256: fixturePolicyMarker("balance-proof"),
159
+ fundingRequirementsSha256: fixturePolicyMarker("requirements"),
160
+ runtimeArtifactSha256: fixturePolicyMarker("runtime-artifact"),
161
+ runtimeTreeSha256: fixturePolicyMarker("runtime-tree"),
162
+ registrySha256: fixturePolicyMarker("registry"),
163
+ profileId: FIXTURE_POLICY_LANE,
164
+ domain: "perps",
165
+ client: "extension",
166
+ proofLane: FIXTURE_POLICY_LANE,
167
+ targetId: FIXTURE_POLICY_LANE,
168
+ subjectDigest: fixturePolicyMarker("subject"),
169
+ selectedIdentitySha256: "",
170
+ runtimeSessionId: "",
171
+ cdpPort: 0,
172
+ cdpTargetId: "",
173
+ extensionOrigin: "",
174
+ operations: [],
175
+ budgets: [],
176
+ requirements: []
177
+ });
178
+ fixturePolicySources.set(base, { artifactsDir: options.artifactsDir });
179
+ return base;
180
+ }
181
+ function isFixturePolicyBase(base) {
182
+ return base?.proofLane === FIXTURE_POLICY_LANE;
183
+ }
184
+ async function authorizeFixturePolicyPlan(base, plan) {
185
+ const source = fixturePolicySources.get(base);
186
+ if (!source) throw new Error("Fixture-policy plan authorization requires the runner-created context.");
187
+ const executionPlanSha256 = normalizeRecipeDigest(plan.digest);
188
+ await mkdir(source.artifactsDir, { recursive: true, mode: 448 });
189
+ const artifactsDir = await realpath(source.artifactsDir);
190
+ const artifactsIdentitySha256 = await directoryIdentity(artifactsDir);
191
+ const executionLedgerRoot = path.join(artifactsDir, "mutation-ledger");
192
+ await ensurePrivateDirectory(artifactsDir, executionLedgerRoot);
193
+ const runId = sha256(canonical({
194
+ lane: FIXTURE_POLICY_LANE,
195
+ rootRecipeSha256: base.rootRecipeSha256,
196
+ executionPlanSha256,
197
+ artifactsIdentitySha256
198
+ }));
199
+ const authorized = deepFreeze({ ...base, runId, executionPlanSha256, artifactsIdentitySha256 });
200
+ fixturePolicySources.set(authorized, {
201
+ ...source,
202
+ executionLedgerRoot,
203
+ executionClaimSha256: sha256(canonical({ kind: "fixture-policy-execution-claim", runId, executionPlanSha256 }))
204
+ });
205
+ return authorized;
206
+ }
144
207
  async function authorizeTrustedMutationPlan(base, plan, testIo = {}) {
208
+ if (isFixturePolicyBase(base)) return authorizeFixturePolicyPlan(base, plan);
145
209
  const source = fundingSources.get(base);
146
210
  if (!source) throw new Error("Funding plan authorization requires the original runner-verified context.");
147
211
  const executionPlanSha256 = normalizeRecipeDigest(plan.digest);
@@ -228,6 +292,20 @@ async function authorizeTrustedMutationPlan(base, plan, testIo = {}) {
228
292
  return authorized;
229
293
  }
230
294
  async function trustedMutationForAction(base, recipe, selectedIdentitySha256) {
295
+ if (isFixturePolicyBase(base)) {
296
+ const source2 = fixturePolicySources.get(base);
297
+ if (!source2?.executionLedgerRoot || !source2.executionClaimSha256) {
298
+ throw new Error("External mutation requires an authorized run-owned execution ledger.");
299
+ }
300
+ const trusted2 = deepFreeze({
301
+ ...base,
302
+ recipeSha256: normalizeRecipeDigest(digestRecipeDocument(recipe)),
303
+ executionLedgerRoot: source2.executionLedgerRoot,
304
+ executionClaimSha256: source2.executionClaimSha256
305
+ });
306
+ trustedActionContexts.add(trusted2);
307
+ return trusted2;
308
+ }
231
309
  if (!selectedIdentitySha256 || !SHA256.test(selectedIdentitySha256)) {
232
310
  throw new Error(
233
311
  "External mutation requires a successful same-run metamask.wallet.import selected identity proof. Next: run metamask.wallet.import before the mutation node."
@@ -255,6 +333,18 @@ async function trustedMutationForAction(base, recipe, selectedIdentitySha256) {
255
333
  trustedActionContexts.add(trusted);
256
334
  return trusted;
257
335
  }
336
+ function fundingOptionsFromCli(options) {
337
+ return {
338
+ fundingRequest: optionString(options, "fundingRequest"),
339
+ fundingPreflight: optionString(options, "fundingPreflight"),
340
+ fundingReleaseProof: optionString(options, "fundingReleaseProof"),
341
+ fundingBalanceProof: optionString(options, "fundingBalanceProof")
342
+ };
343
+ }
344
+ function trustedMutationContextExtras(context) {
345
+ const trusted = context.trustedMutation;
346
+ return trusted ? { trustedMutation: serializeTrustedMutationContext(trusted) } : void 0;
347
+ }
258
348
  function serializeTrustedMutationContext(value) {
259
349
  if (!isRecord(value) || !trustedActionContexts.has(value)) {
260
350
  throw new Error("Live adapter refused an untrusted mutation context.");
@@ -1430,9 +1520,14 @@ function deepFreeze(value) {
1430
1520
  return value;
1431
1521
  }
1432
1522
  export {
1523
+ FIXTURE_POLICY_LANE,
1433
1524
  authorizeTrustedMutationPlan,
1525
+ fixturePolicyMutationBase,
1526
+ fundingOptionsFromCli,
1527
+ isFixturePolicyBase,
1434
1528
  loadTrustedMutationBase,
1435
1529
  selectedIdentityFromWalletImport,
1436
1530
  serializeTrustedMutationContext,
1531
+ trustedMutationContextExtras,
1437
1532
  trustedMutationForAction
1438
1533
  };
package/dist/host.js ADDED
@@ -0,0 +1,32 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { configureHarnessAdapters, configureHarnessHost } from "@farmslot/recipe-cli/harness";
4
+ import { adapterRegistry } from "./adapters/registry.js";
5
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
+ configureHarnessHost({
7
+ name: "mm-harness",
8
+ product: "MetaMask",
9
+ envPrefix: "MM_HARNESS",
10
+ recipeEnvPrefix: "METAMASK_RECIPE",
11
+ packageName: "@deeeed/metamask-harness",
12
+ packageRoot,
13
+ bin: "bin/mm-harness",
14
+ journaledCommands: [
15
+ "prepare",
16
+ "call",
17
+ "check",
18
+ "checklist",
19
+ "cleanup",
20
+ "doctor",
21
+ "fixtures",
22
+ "install",
23
+ "launch",
24
+ "runtime-launch",
25
+ "provision",
26
+ "recipe-quality",
27
+ "run",
28
+ "stop",
29
+ "verify"
30
+ ]
31
+ });
32
+ configureHarnessAdapters(adapterRegistry);
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "./host.js";
1
2
  import { createDoctorReport } from "./doctor.js";
2
3
  import { loadActionManifest, validateManifest } from "./manifest.js";
3
4
  import { createMetaMaskExtensionRunner, createMetaMaskMobileRunner, createMetaMaskRunner } from "./runner.js";
@@ -5,13 +6,9 @@ import {
5
6
  extensionIdPath,
6
7
  importRecipeHarnessRuntimeBrowserExtension,
7
8
  importRecipeHarnessRuntimeCdp,
8
- recipeHarnessPath,
9
9
  walletFixturePath
10
10
  } from "./paths.js";
11
- import { captureActiveRecipeRecordingSnapshot } from "./run-recording.js";
12
- import { validateRuntimeProof } from "./runtime-proof.js";
13
11
  export {
14
- captureActiveRecipeRecordingSnapshot,
15
12
  createDoctorReport,
16
13
  createMetaMaskExtensionRunner,
17
14
  createMetaMaskMobileRunner,
@@ -20,8 +17,6 @@ export {
20
17
  importRecipeHarnessRuntimeBrowserExtension,
21
18
  importRecipeHarnessRuntimeCdp,
22
19
  loadActionManifest,
23
- recipeHarnessPath,
24
20
  validateManifest,
25
- validateRuntimeProof,
26
21
  walletFixturePath
27
22
  };
@@ -0,0 +1,49 @@
1
+ import { accessSync, constants, existsSync, readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ const requireFromHarness = createRequire(import.meta.url);
5
+ function harnessTsxBin() {
6
+ try {
7
+ const manifest = requireFromHarness.resolve("tsx/package.json");
8
+ const { bin } = JSON.parse(readFileSync(manifest, "utf8"));
9
+ const relative = typeof bin === "string" ? bin : bin?.tsx;
10
+ return relative ? path.join(path.dirname(manifest), relative) : void 0;
11
+ } catch {
12
+ return void 0;
13
+ }
14
+ }
15
+ function liveTsxCandidates() {
16
+ const bin = harnessTsxBin();
17
+ return bin ? [bin] : [];
18
+ }
19
+ function isExecutable(file) {
20
+ try {
21
+ accessSync(file, constants.X_OK);
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ function resolveOverride(override, projectRoot) {
28
+ if (!override.includes("/")) {
29
+ return (process.env.PATH ?? "").split(path.delimiter).map((dir) => path.join(path.resolve(projectRoot, dir || "."), override)).find(isExecutable);
30
+ }
31
+ const bin = path.resolve(projectRoot, override);
32
+ return isExecutable(bin) ? bin : void 0;
33
+ }
34
+ function resolveLiveTsx(projectRoot) {
35
+ const override = process.env.TSX_BIN;
36
+ if (override) {
37
+ const bin = resolveOverride(override, path.resolve(projectRoot));
38
+ return bin ? { bin, source: "TSX_BIN" } : null;
39
+ }
40
+ const target = path.join(path.resolve(projectRoot), "node_modules/.bin/tsx");
41
+ if (existsSync(target)) return { bin: target, source: "target" };
42
+ const harness = harnessTsxBin();
43
+ return harness && existsSync(harness) ? { bin: harness, source: "harness" } : null;
44
+ }
45
+ export {
46
+ harnessTsxBin,
47
+ liveTsxCandidates,
48
+ resolveLiveTsx
49
+ };
@@ -1,19 +1,17 @@
1
+ import "./host.js";
1
2
  import { spawnSync } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { Command } from "commander";
6
- import { color } from "./cli-color.js";
7
- import { withCommandJournal } from "./command-journal.js";
8
- import { JsonStreamWriter } from "./json-stream.js";
7
+ import { color, withCommandJournal, JsonStreamWriter, detectAdapter, handleCallHelp } from "@farmslot/recipe-cli/harness";
9
8
  import { handleUpdate, maybeNudge } from "./commands/update.js";
10
9
  import { handlePin } from "./commands/pin.js";
11
- import { handleCallHelp } from "./commands/call.js";
10
+ import { metaMaskEngine } from "./engine.js";
12
11
  import { handleHelp } from "./commands/help.js";
13
12
  import { handleTutorial } from "./commands/tutorial.js";
14
13
  import { handleSetupBase } from "./commands/setup-base.js";
15
14
  import { getAdapterSurface } from "./adapters/surface.js";
16
- import { detectAdapter } from "./harness.js";
17
15
  import { configPath, hydrateLibraryPathEnv, readConfig } from "./review/knowledge.js";
18
16
  import {
19
17
  PUBLIC_COMMAND_CONTRACTS,
@@ -1233,6 +1231,6 @@ if (hasPassthroughHelp(rawArgv)) {
1233
1231
  }
1234
1232
  if (isCallActionHelp(rawArgv)) {
1235
1233
  const callHelp = REAL.find((command) => command.name === "call")?.helpText ?? "";
1236
- process.exit(await handleCallHelp(rawArgv.slice(1), callHelp));
1234
+ process.exit(await handleCallHelp(rawArgv.slice(1), callHelp, { catalog: metaMaskEngine }));
1237
1235
  }
1238
1236
  await program.parseAsync(process.argv);
package/dist/paths.js CHANGED
@@ -1,43 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { recipeRuntimeDir, recipeRuntimePath } from "@farmslot/recipe-cli/harness";
4
5
  import { adapterRegistry } from "./adapters/registry.js";
5
6
  const runnerDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
- const pathDefaults = readPathDefaults();
7
- const DEFAULT_RECIPE_RUNTIME_DIR = pathDefaults.recipeRuntimeDir;
8
- const DEFAULT_RECIPE_HARNESS_ROOT = pathDefaults.recipeHarnessRoot;
9
7
  const PREPARE_PROGRESS_ARTIFACT = "prepare/progress.json";
10
- function recipeRuntimeDir() {
11
- return validateRelativeRecipePath("RECIPE_RUNTIME_DIR", process.env.RECIPE_RUNTIME_DIR || DEFAULT_RECIPE_RUNTIME_DIR);
12
- }
13
- function recipeHarnessRoot() {
14
- return validateRelativeRecipePath("RECIPE_HARNESS_ROOT", process.env.RECIPE_HARNESS_ROOT || DEFAULT_RECIPE_HARNESS_ROOT);
15
- }
16
- function harnessExecutable() {
17
- return path.resolve(process.env.MM_HARNESS_EXECUTABLE ?? path.join(runnerDir, "bin/mm-harness"));
18
- }
19
- function readPathDefaults() {
20
- const defaultsPath = path.join(runnerDir, "adapters/shared/path-defaults.json");
21
- const parsed = JSON.parse(fs.readFileSync(defaultsPath, "utf8"));
22
- return {
23
- recipeRuntimeDir: validateRelativeRecipePath("recipeRuntimeDir", parsed.recipeRuntimeDir || ""),
24
- recipeHarnessRoot: validateRelativeRecipePath("recipeHarnessRoot", parsed.recipeHarnessRoot || "")
25
- };
26
- }
27
- function validateRelativeRecipePath(name, value) {
28
- if (!value || path.isAbsolute(value)) throw new Error(`${name} must be a non-empty relative path: ${value}`);
29
- if (!/^[A-Za-z0-9._/-]+$/u.test(value)) throw new Error(`${name} contains unsupported characters: ${value}`);
30
- for (const part of value.split("/")) {
31
- if (!part || part === "." || part === "..") throw new Error(`${name} contains unsafe path component: ${value}`);
32
- }
33
- return value;
34
- }
35
- function recipeRuntimePath(projectRoot, ...segments) {
36
- return path.join(projectRoot, recipeRuntimeDir(), ...segments);
37
- }
38
- function recipeHarnessPath(projectRoot, ...segments) {
39
- return path.join(projectRoot, recipeHarnessRoot(), ...segments);
40
- }
41
8
  function walletFixturePath(projectRoot) {
42
9
  return recipeRuntimePath(projectRoot, "wallet-fixture.json");
43
10
  }
@@ -51,42 +18,51 @@ function recipeWatchLogCandidates() {
51
18
  ];
52
19
  }
53
20
  function resolveLocalProtocolRoot() {
54
- const candidates = [
55
- process.env.METAMASK_RUNNER_PROTOCOL_ROOT,
56
- readConfiguredProtocolRoot(),
57
- findProtocolRoot(runnerDir),
58
- findProtocolRoot(process.cwd())
59
- ].filter(Boolean);
60
- const root = candidates[0];
21
+ const root = process.env.METAMASK_RUNNER_PROTOCOL_ROOT || readConfiguredProtocolRoot();
61
22
  return root ? path.resolve(root) : void 0;
62
23
  }
63
- function resolveRequiredLocalProtocolRoot(reason) {
64
- const root = resolveLocalProtocolRoot();
65
- if (!root) {
66
- throw new Error(
67
- `${reason} requires a local protocol/runtime checkout. Set METAMASK_RUNNER_PROTOCOL_ROOT or create .farmslot-root for this dev-only path.`
68
- );
24
+ function resolveFarmslotPoolDir(starts = [runnerDir, process.cwd()]) {
25
+ const configured = resolveLocalProtocolRoot();
26
+ if (configured) return path.join(configured, "pool");
27
+ for (const start of starts) {
28
+ const pool = findPoolDir(start);
29
+ if (pool) return pool;
69
30
  }
70
- return root;
31
+ return void 0;
71
32
  }
33
+ const LIBRARY_ACTION_NAMESPACE = "metamask";
72
34
  function readConfiguredProtocolRoot() {
73
35
  const configPath = path.join(runnerDir, ".farmslot-root");
74
36
  if (!fs.existsSync(configPath)) return void 0;
75
37
  const value = fs.readFileSync(configPath, "utf8").trim();
76
38
  return value || void 0;
77
39
  }
78
- function findProtocolRoot(start) {
40
+ function findPoolDir(start) {
79
41
  let dir = path.resolve(start);
80
42
  while (dir !== path.dirname(dir)) {
81
- if (isProtocolRoot(dir)) return dir;
82
- const sibling = path.join(dir, "farmslot");
83
- if (isProtocolRoot(sibling)) return sibling;
43
+ for (const root of [dir, path.join(dir, "farmslot")]) {
44
+ const pool = path.join(root, "pool");
45
+ if (holdsSlotPool(pool)) return pool;
46
+ }
84
47
  dir = path.dirname(dir);
85
48
  }
86
49
  return void 0;
87
50
  }
88
- function isProtocolRoot(candidate) {
89
- return fs.existsSync(path.join(candidate, "packages/recipe-runner/package.json")) && fs.existsSync(path.join(candidate, "packages/protocol/package.json"));
51
+ function holdsSlotPool(candidate) {
52
+ let names;
53
+ try {
54
+ names = fs.readdirSync(candidate);
55
+ } catch {
56
+ return false;
57
+ }
58
+ return names.some((name) => {
59
+ if (!name.endsWith(".json") || name.includes(".bak")) return false;
60
+ try {
61
+ return Array.isArray(JSON.parse(fs.readFileSync(path.join(candidate, name), "utf8"))?.slots);
62
+ } catch {
63
+ return false;
64
+ }
65
+ });
90
66
  }
91
67
  function assertAdapter(adapter) {
92
68
  const ids = adapterRegistry.list();
@@ -140,8 +116,8 @@ async function importRecipeHarnessRuntimeReactNativeBridge() {
140
116
  }
141
117
  async function importMetaMaskNativeUiProvider() {
142
118
  return importProtocolPackage(
143
- "@farmslot/expo-recipe",
144
- "packages/expo-recipe/src/index.ts"
119
+ "@farmslot/adapter-rn",
120
+ "packages/adapter-rn/src/index.ts"
145
121
  );
146
122
  }
147
123
  async function importRecipeRunnerCli() {
@@ -176,12 +152,10 @@ function isMissingPackageError(error, packageName) {
176
152
  return (code === "ERR_MODULE_NOT_FOUND" || code === "ERR_PACKAGE_PATH_NOT_EXPORTED") && error.message.includes(packageName);
177
153
  }
178
154
  export {
179
- DEFAULT_RECIPE_HARNESS_ROOT,
180
- DEFAULT_RECIPE_RUNTIME_DIR,
155
+ LIBRARY_ACTION_NAMESPACE,
181
156
  PREPARE_PROGRESS_ARTIFACT,
182
157
  assertAdapter,
183
158
  extensionIdPath,
184
- harnessExecutable,
185
159
  importMetaMaskNativeUiProvider,
186
160
  importRecipeHarness,
187
161
  importRecipeHarnessAppLifecycle,
@@ -192,14 +166,10 @@ export {
192
166
  importRecipeRunnerCli,
193
167
  manifestPath,
194
168
  readJson,
195
- recipeHarnessPath,
196
- recipeHarnessRoot,
197
169
  recipePath,
198
- recipeRuntimeDir,
199
- recipeRuntimePath,
200
170
  recipeWatchLogCandidates,
171
+ resolveFarmslotPoolDir,
201
172
  resolveLocalProtocolRoot,
202
- resolveRequiredLocalProtocolRoot,
203
173
  runnerDir,
204
174
  walletFixturePath
205
175
  };
@@ -4,20 +4,18 @@ import { gzip } from "node:zlib";
4
4
  import { getAdapterSurface } from "./adapters/surface.js";
5
5
  import { createExtensionPerformanceBackend } from "./adapters/extension/performance-observer.js";
6
6
  import { TRACE_KINDS } from "./adapters/performance/cdp-trace.js";
7
- import { summarizeFrames } from "./adapters/mobile/frame-metrics.js";
7
+ import { summarizeFrames } from "@farmslot/adapter-rn";
8
8
  import { summarizeJavaScriptTasks } from "./adapters/performance/js-task-metrics.js";
9
9
  import {
10
10
  indexArtifactManifest,
11
11
  readContainedJsonArtifact,
12
12
  writeContainedArtifact
13
- } from "./artifact-files.js";
13
+ } from "@farmslot/recipe-cli/harness";
14
14
  const sessions = /* @__PURE__ */ new Map();
15
15
  const DEFAULT_MAX_DURATION_MS = 5 * 60 * 1e3;
16
16
  const MAX_DURATION_MS = 60 * 60 * 1e3;
17
17
  const gzipAsync = promisify(gzip);
18
- function startRunPerformanceObservation(adapter, target, artifactsDir, env, ports = {}) {
19
- const observer = getAdapterSurface(adapter).observation?.performance;
20
- if (!observer) return void 0;
18
+ function startRunPerformanceObservation(observer, { target, artifactsDir, env, ports }) {
21
19
  const key = path.resolve(artifactsDir);
22
20
  const session = {
23
21
  observer,
package/dist/progress.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { parseArgs } from "node:util";
3
- import { color } from "./cli-color.js";
3
+ import { color } from "@farmslot/recipe-cli/harness";
4
4
  function shouldEmitJson(explicit) {
5
5
  if (explicit) return true;
6
6
  const env = process.env.RECIPE_PROGRESS_JSON;
@@ -1,13 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import { createHash } from "node:crypto";
3
3
  import { loadRecipeLibraries, resolveRecipeDependencies, rootResolutionRef } from "@farmslot/recipe-runner";
4
- import { isSensitiveKey } from "./command-journal.js";
4
+ import { isSensitiveKey, CliError, describeManifestActions, isRecord, optionStrings, EXIT, resolveLibrarySources, validateRunRecipeStatic } from "@farmslot/recipe-cli/harness";
5
5
  import { resolveActionManifest } from "./manifest.js";
6
- import { describeManifestActions } from "./commands/manifest.js";
7
6
  import { recipeComposition } from "@farmslot/recipe-cli";
8
- import { resolveMetaMaskLibrarySources, validateRunRecipeStatic } from "./commands/run-engine.js";
9
- import { CliError, isRecord, optionStrings } from "./commands/parse-args.js";
10
- import { EXIT } from "./commands/shared.js";
7
+ import { metaMaskEngine } from "./engine.js";
11
8
  import { MAX_ADVICE_STATE_BYTES } from "./recipe-assistance-rubric.js";
12
9
  function digest(value) {
13
10
  return createHash("sha256").update(value).digest("hex");
@@ -48,7 +45,7 @@ async function buildAdviceContext(input) {
48
45
  const task = readInput(input.task);
49
46
  if (!input.recipe) throw new CliError("Advice requires a recipe. Next: use recipe-quality plan before authoring.", EXIT.usage);
50
47
  readInput(input.recipe);
51
- const validated = await validateRunRecipeStatic(input.recipe, input.adapter, input.options ?? {}, input.params);
48
+ const validated = await validateRunRecipeStatic(metaMaskEngine, input.recipe, input.adapter, input.options ?? {}, input.params);
52
49
  if (validated.usageError || !validated.schemaValid || !isRecord(validated.recipe)) {
53
50
  const detail = validated.usageError?.message ?? validated.findings.map((finding) => finding.message).join("; ");
54
51
  throw new CliError(`Recipe is not statically valid: ${detail}. Next: run mm-harness run ${input.recipe} --plan --adapter ${input.adapter}.`, EXIT.validation);
@@ -58,7 +55,7 @@ async function buildAdviceContext(input) {
58
55
  const dependencies = resolveRecipeDependencies({ rootRef: rootResolutionRef(rootDigest), root: validated.recipe, rootSource: { kind: "recipe-file", trust: "unknown", digest: rootDigest }, recipes: library.recipes });
59
56
  const composition = recipeComposition(validated.recipe, dependencies.recipes);
60
57
  const { manifest, actionSources } = await resolveActionManifest(input.adapter, void 0, validated.librarySources);
61
- const catalog = describeManifestActions(manifest, actionSources);
58
+ const catalog = describeManifestActions(metaMaskEngine, manifest, actionSources);
62
59
  const used = new Set(composition.actions);
63
60
  const actions = catalog.filter((action) => used.has(action.name)).map(({ name, description, schema, capabilities }) => ({ name, description, schema, capabilities }));
64
61
  const domains = new Set(composition.actions.map((name) => name.split(".").slice(0, -1).join(".")));
@@ -110,12 +107,12 @@ async function buildPlanningContext(input) {
110
107
  throw new CliError("Planning requires 1\u201320 non-empty acceptanceCriteria strings. Next: supply a focused JSON task.", EXIT.validation);
111
108
  }
112
109
  const criteria = task.acceptanceCriteria;
113
- const sources = await resolveMetaMaskLibrarySources(optionStrings(input.options ?? {}, "library"));
110
+ const sources = await resolveLibrarySources(metaMaskEngine, optionStrings(input.options ?? {}, "library"));
114
111
  const library = await loadRecipeLibraries(sources ?? [], { adapter: input.adapter });
115
112
  const { manifest, actionSources } = await resolveActionManifest(input.adapter, void 0, sources);
116
113
  const candidates = [
117
114
  ...[...library.recipes.values()].map((recipe) => ({ id: `recipe:${recipe.ref}`, kind: "recipe", ref: recipe.ref, description: typeof recipe.document.description === "string" ? recipe.document.description : "", parameters: recipe.document.paramsSchema, source: recipe.source, provenance: recipe.provenance, shadows: recipe.shadows })),
118
- ...describeManifestActions(manifest, actionSources).map((action) => ({ id: `action:${action.name}`, kind: "action", ref: action.name, description: action.description, parameters: action.schema, capabilities: action.capabilities, source: action.source, sourceTier: action.sourceTier, shadows: action.shadows }))
115
+ ...describeManifestActions(metaMaskEngine, manifest, actionSources).map((action) => ({ id: `action:${action.name}`, kind: "action", ref: action.name, description: action.description, parameters: action.schema, capabilities: action.capabilities, source: action.source, sourceTier: action.sourceTier, shadows: action.shadows }))
119
116
  ];
120
117
  const terms = new Set(criteria.join(" ").toLowerCase().match(/[a-z][a-z_]{3,}/gu) ?? []);
121
118
  const score = (candidate) => [...new Set(`${candidate.ref} ${candidate.description}`.toLowerCase().match(/[a-z][a-z_]{3,}/gu) ?? [])].filter((word) => terms.has(word)).length;