@deeeed/metamask-harness 0.18.0 → 0.19.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 (79) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/README.md +4 -7
  3. package/adapters/core/inject.sh +1 -6
  4. package/adapters/extension/inject.mjs +1 -4
  5. package/adapters/extension/verify.sh +1 -1
  6. package/adapters/mobile/inject.sh +5 -4
  7. package/adapters/mobile/verify.sh +15 -19
  8. package/adapters/shared/harness-source-fingerprint.mjs +14 -12
  9. package/dist/adapters/harness-freshness.js +49 -0
  10. package/dist/cli.js +0 -4
  11. package/dist/command-contract.js +51 -40
  12. package/dist/command-journal.js +50 -12
  13. package/dist/commands/call.js +95 -20
  14. package/dist/commands/check.js +1 -1
  15. package/dist/commands/completion-candidates.js +7 -19
  16. package/dist/commands/fixtures.js +50 -1
  17. package/dist/commands/last.js +9 -1
  18. package/dist/commands/launch/extension.js +2 -2
  19. package/dist/commands/launch/mobile.js +2 -0
  20. package/dist/commands/list-executables.js +56 -20
  21. package/dist/commands/manifest.js +28 -12
  22. package/dist/commands/parse-args.js +55 -3
  23. package/dist/commands/provision.js +0 -1
  24. package/dist/commands/run-engine.js +514 -336
  25. package/dist/commands/run.js +25 -37
  26. package/dist/commands/shared.js +1 -1
  27. package/dist/heal-bounds.js +5 -0
  28. package/dist/mm-harness-cli.js +22 -30
  29. package/dist/run-diagnostics.js +1 -1
  30. package/dist/run-recording.js +1 -1
  31. package/dist/runner.js +24 -2
  32. package/docs/CONTRIBUTING.md +2 -3
  33. package/docs/QA.md +0 -1
  34. package/docs/RECIPES.md +52 -91
  35. package/library/README.md +5 -5
  36. package/library/actions/core/perps/_controller.mjs +33 -1
  37. package/library/actions/core/perps/assert_orders.mjs +6 -7
  38. package/library/actions/core/perps/assert_positions.mjs +6 -7
  39. package/library/actions/core/perps/close_orders.mjs +2 -0
  40. package/library/actions/core/perps/close_positions.mjs +2 -0
  41. package/library/actions/core/perps/ensure_orders.mjs +4 -2
  42. package/library/actions/core/perps/ensure_positions.mjs +4 -2
  43. package/library/actions/core/perps/place_order.mjs +7 -3
  44. package/library/actions/extension/perps/assert_orders.mjs +2 -1
  45. package/library/actions/extension/perps/assert_positions.mjs +2 -1
  46. package/library/actions/extension/perps/perps.mjs +43 -14
  47. package/library/actions/mobile/perps/assert_orders.mjs +2 -1
  48. package/library/actions/mobile/perps/assert_positions.mjs +2 -1
  49. package/library/actions/mobile/perps/perps.mjs +40 -12
  50. package/library/library.json +1 -1
  51. package/library/manifests/core.action-manifest.json +1170 -413
  52. package/library/manifests/extension.action-manifest.json +1495 -641
  53. package/library/manifests/mobile.action-manifest.json +1704 -769
  54. package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
  55. package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
  56. package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
  57. package/library/recipes/perps/lifecycle.recipe.json +136 -180
  58. package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
  59. package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
  60. package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
  61. package/library/recipes/perps/performance.mobile.recipe.json +37 -51
  62. package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
  63. package/library/recipes/perps/read-markets.core.recipe.json +29 -31
  64. package/library/recipes/perps/smoke.core.recipe.json +29 -32
  65. package/library/recipes/perps/smoke.extension.recipe.json +41 -44
  66. package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
  67. package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
  68. package/library/recipes/runner/action-validation.extension.recipe.json +312 -405
  69. package/library/recipes/runner/action-validation.mobile.recipe.json +316 -409
  70. package/library/recipes/runner/smoke.core.recipe.json +18 -20
  71. package/library/recipes/runner/smoke.extension.recipe.json +23 -24
  72. package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
  73. package/library/recipes/wallet/smoke.extension.recipe.json +33 -35
  74. package/library/recipes/wallet/smoke.mobile.recipe.json +33 -35
  75. package/package.json +3 -3
  76. package/scripts/completions.sh +1 -4
  77. package/dist/adapters/extension/harness-freshness.js +0 -39
  78. package/dist/commands/flows.js +0 -91
  79. package/library/flows/perps.flows.json +0 -64
@@ -1,26 +1,24 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Core runner smoke",
4
4
  "description": "Proves the headless Core runner resolves the checkout, executes generic actions, and writes a valid artifact package.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "next": "package",
12
- "intent": "Resolve the Core checkout and report headless readiness"
13
- },
14
- "package": {
15
- "action": "assert_file",
16
- "path": "package.json",
17
- "next": "done",
18
- "intent": "Prove generic project-file assertions work in Core"
19
- },
20
- "done": {
21
- "action": "end",
22
- "status": "pass"
23
- }
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "next": "package",
11
+ "intent": "Resolve the Core checkout and report headless readiness"
12
+ },
13
+ "package": {
14
+ "action": "assert_file",
15
+ "path": "package.json",
16
+ "next": "done",
17
+ "intent": "Prove generic project-file assertions work in Core"
18
+ },
19
+ "done": {
20
+ "action": "end",
21
+ "status": "pass"
24
22
  }
25
23
  }
26
24
  }
@@ -1,30 +1,29 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Extension v1 runner smoke",
4
4
  "description": "Proves the MetaMask Extension runner executes manifest-declared actions and writes a valid artifact package.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "next": "fixture",
12
- "intent": "Perform status for the recipe"
13
- },
14
- "fixture": {
15
- "action": "metamask.wallet.fixture_status",
16
- "next": "cdp",
17
- "intent": "Perform fixture for the recipe"
18
- },
19
- "cdp": {
20
- "action": "cdp.target",
21
- "next": "done",
22
- "intent": "Perform cdp for the recipe"
23
- },
24
- "done": {
25
- "action": "end",
26
- "status": "pass"
27
- }
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "next": "fixture",
11
+ "intent": "Confirm the Extension checkout is ready"
12
+ },
13
+ "fixture": {
14
+ "action": "metamask.wallet.fixture_status",
15
+ "next": "cdp",
16
+ "intent": "Confirm the wallet fixture is available"
17
+ },
18
+ "cdp": {
19
+ "action": "cdp.target",
20
+ "required": true,
21
+ "next": "done",
22
+ "intent": "Confirm the Extension runtime is reachable"
23
+ },
24
+ "done": {
25
+ "action": "end",
26
+ "status": "pass"
28
27
  }
29
28
  }
30
29
  }
@@ -1,30 +1,29 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Mobile v1 runner smoke",
4
4
  "description": "Proves the MetaMask Mobile runner executes manifest-declared actions and writes a valid artifact package.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "next": "fixture",
12
- "intent": "Perform status for the recipe"
13
- },
14
- "fixture": {
15
- "action": "metamask.wallet.fixture_status",
16
- "next": "cdp",
17
- "intent": "Perform fixture for the recipe"
18
- },
19
- "cdp": {
20
- "action": "cdp.target",
21
- "next": "done",
22
- "intent": "Perform cdp for the recipe"
23
- },
24
- "done": {
25
- "action": "end",
26
- "status": "pass"
27
- }
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "next": "fixture",
11
+ "intent": "Confirm the Mobile checkout is ready"
12
+ },
13
+ "fixture": {
14
+ "action": "metamask.wallet.fixture_status",
15
+ "next": "cdp",
16
+ "intent": "Confirm the wallet fixture is available"
17
+ },
18
+ "cdp": {
19
+ "action": "cdp.target",
20
+ "require_reachable": true,
21
+ "next": "done",
22
+ "intent": "Confirm the Mobile runtime is reachable"
23
+ },
24
+ "done": {
25
+ "action": "end",
26
+ "status": "pass"
28
27
  }
29
28
  }
30
29
  }
@@ -1,41 +1,39 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Extension wallet smoke",
4
4
  "description": "Fixture-backed proof that the Extension wallet is reachable, unlocked, readable, and visually capturable without depending on a product feature domain.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "next": "fixture",
12
- "intent": "Confirm the Extension runtime is reachable"
13
- },
14
- "fixture": {
15
- "action": "metamask.wallet.fixture_status",
16
- "next": "unlock",
17
- "intent": "Confirm the checkout wallet fixture is ready"
18
- },
19
- "unlock": {
20
- "action": "metamask.wallet.ensure_unlocked",
21
- "next": "state",
22
- "intent": "Ensure the fixture-backed wallet is unlocked"
23
- },
24
- "state": {
25
- "action": "metamask.wallet.read_state",
26
- "next": "screenshot",
27
- "intent": "Read the current wallet state through the public adapter"
28
- },
29
- "screenshot": {
30
- "action": "ui.screenshot",
31
- "path": "screenshots/wallet-smoke.png",
32
- "next": "done",
33
- "intent": "Capture visual wallet evidence"
34
- },
35
- "done": {
36
- "action": "end",
37
- "status": "pass"
38
- }
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "next": "fixture",
11
+ "intent": "Confirm the Extension runtime is reachable"
12
+ },
13
+ "fixture": {
14
+ "action": "metamask.wallet.fixture_status",
15
+ "next": "unlock",
16
+ "intent": "Confirm the checkout wallet fixture is ready"
17
+ },
18
+ "unlock": {
19
+ "action": "metamask.wallet.ensure_unlocked",
20
+ "next": "state",
21
+ "intent": "Ensure the fixture-backed wallet is unlocked"
22
+ },
23
+ "state": {
24
+ "action": "metamask.wallet.read_state",
25
+ "next": "screenshot",
26
+ "intent": "Read the current wallet state through the public adapter"
27
+ },
28
+ "screenshot": {
29
+ "action": "ui.screenshot",
30
+ "path": "screenshots/wallet-smoke.png",
31
+ "next": "done",
32
+ "intent": "Record the visible wallet state as evidence"
33
+ },
34
+ "done": {
35
+ "action": "end",
36
+ "status": "pass"
39
37
  }
40
38
  }
41
39
  }
@@ -1,41 +1,39 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Mobile wallet smoke",
4
4
  "description": "Fixture-backed proof that the Mobile wallet is reachable, unlocked, readable, and visually capturable without depending on a product feature domain.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "next": "fixture",
12
- "intent": "Confirm the Mobile runtime is reachable"
13
- },
14
- "fixture": {
15
- "action": "metamask.wallet.fixture_status",
16
- "next": "unlock",
17
- "intent": "Confirm the checkout wallet fixture is ready"
18
- },
19
- "unlock": {
20
- "action": "metamask.wallet.ensure_unlocked",
21
- "next": "state",
22
- "intent": "Ensure the fixture-backed wallet is unlocked"
23
- },
24
- "state": {
25
- "action": "metamask.wallet.read_state",
26
- "next": "screenshot",
27
- "intent": "Read the current wallet state through the public adapter"
28
- },
29
- "screenshot": {
30
- "action": "ui.screenshot",
31
- "path": "screenshots/wallet-smoke.png",
32
- "next": "done",
33
- "intent": "Capture visual wallet evidence"
34
- },
35
- "done": {
36
- "action": "end",
37
- "status": "pass"
38
- }
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "next": "fixture",
11
+ "intent": "Confirm the Mobile runtime is reachable"
12
+ },
13
+ "fixture": {
14
+ "action": "metamask.wallet.fixture_status",
15
+ "next": "unlock",
16
+ "intent": "Confirm the checkout wallet fixture is ready"
17
+ },
18
+ "unlock": {
19
+ "action": "metamask.wallet.ensure_unlocked",
20
+ "next": "state",
21
+ "intent": "Ensure the fixture-backed wallet is unlocked"
22
+ },
23
+ "state": {
24
+ "action": "metamask.wallet.read_state",
25
+ "next": "screenshot",
26
+ "intent": "Read the current wallet state through the public adapter"
27
+ },
28
+ "screenshot": {
29
+ "action": "ui.screenshot",
30
+ "path": "screenshots/wallet-smoke.png",
31
+ "next": "done",
32
+ "intent": "Record the visible wallet state as evidence"
33
+ },
34
+ "done": {
35
+ "action": "end",
36
+ "status": "pass"
39
37
  }
40
38
  }
41
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -20,8 +20,8 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@farmslot/agent-runtime": "^0.2.0",
23
- "@farmslot/protocol": "^0.10.0",
24
- "@farmslot/recipe-harness": "^0.8.0",
23
+ "@farmslot/protocol": "^0.11.0",
24
+ "@farmslot/recipe-harness": "^0.9.0",
25
25
  "commander": "^12.0.0",
26
26
  "es-module-lexer": "2.3.1",
27
27
  "esbuild": "0.28.1",
@@ -29,7 +29,7 @@ _mmh_bin() {
29
29
  return 1
30
30
  }
31
31
 
32
- _mmh_commands="launch stop logs debug fixtures actions call flows run doctor check checklist recipe-quality install verify cleanup completions"
32
+ _mmh_commands="launch stop logs debug fixtures actions call run doctor check checklist recipe-quality install verify cleanup completions"
33
33
 
34
34
  # Per-command flags (static, from the mm-harness surface).
35
35
  _mmh_flags_for() {
@@ -42,7 +42,6 @@ _mmh_flags_for() {
42
42
  checklist) printf '%s' "" ;;
43
43
  actions) printf '%s' "--adapter --action --kind --raw --action-manifest --target --json" ;;
44
44
  call) printf '%s' "--list --arg --adapter --target --artifacts-dir --action-manifest --heal --json" ;;
45
- flows) printf '%s' "--library --json" ;;
46
45
  run) printf '%s' "--list --describe --plan --adapter --artifacts-dir --target --library --cdp-port --heal --record-video --json" ;;
47
46
  doctor) printf '%s' "--adapter --target --fix --json" ;;
48
47
  install|verify|cleanup) printf '%s' "--adapter --target --json" ;;
@@ -80,7 +79,6 @@ if [ -n "${BASH_VERSION:-}" ]; then
80
79
  fixtures) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "init sync set generate finalize" -- "$cur")) ;;
81
80
  checklist) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "mark" -- "$cur")) ;;
82
81
  completions) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "install bash zsh status" -- "$cur")) ;;
83
- flows) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "list promote" -- "$cur")) ;;
84
82
  call) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "$(_mmh_actions)" -- "$cur")) ;;
85
83
  run) COMPREPLY=($(compgen -f -- "$cur"; compgen -W "$(_mmh_recipes)" -- "$cur")) ;;
86
84
  *) COMPREPLY=($(compgen -W "$(_mmh_flags_for "$cmd")" -- "$cur")) ;;
@@ -115,7 +113,6 @@ if [ -n "${ZSH_VERSION:-}" ]; then
115
113
  fixtures) (( CURRENT == 3 )) && { candidates=(init sync set generate finalize); compadd -a candidates; } ;;
116
114
  checklist) (( CURRENT == 3 )) && { candidates=(mark); compadd -a candidates; } ;;
117
115
  completions) (( CURRENT == 3 )) && { candidates=(install bash zsh status); compadd -a candidates; } ;;
118
- flows) (( CURRENT == 3 )) && { candidates=(list promote); compadd -a candidates; } ;;
119
116
  call) (( CURRENT == 3 )) && { candidates=("${(@f)$(_mmh_actions)}"); compadd -a candidates; } ;;
120
117
  run) candidates=("${(@f)$(_mmh_recipes)}"); compadd -a candidates; _files ;;
121
118
  *) candidates=(${=$(_mmh_flags_for "$cmd")}); compadd -a candidates ;;
@@ -1,39 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { pathToFileURL } from "node:url";
5
- import { recipeHarnessPath, runnerDir } from "../../paths.js";
6
- async function ensureExtensionHarnessFresh(target) {
7
- if (!fs.existsSync(path.join(target, "package.json"))) return;
8
- let current;
9
- try {
10
- const mod = await import(pathToFileURL(path.join(runnerDir, "adapters/shared/harness-source-fingerprint.mjs")).href);
11
- current = mod.harnessSourceFingerprint(runnerDir);
12
- } catch {
13
- return;
14
- }
15
- const manifestPath = recipeHarnessPath(target, "extension", "manifest.json");
16
- const installed = readInstalledFingerprint(manifestPath);
17
- if (installed === current) return;
18
- const reason = installed === null ? "harness not installed" : "runner changed";
19
- try {
20
- execFileSync(process.execPath, [path.join(runnerDir, "adapters/extension/inject.mjs"), "--target", target], {
21
- stdio: ["ignore", "ignore", "inherit"]
22
- });
23
- console.error(`\u2192 refreshed extension harness overlay (${reason})`);
24
- } catch (error) {
25
- const detail = error instanceof Error ? `: ${error.message}` : "";
26
- throw new Error(`could not refresh the extension harness overlay (${reason})${detail}`);
27
- }
28
- }
29
- function readInstalledFingerprint(manifestPath) {
30
- try {
31
- const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
32
- return typeof manifest.sourceFingerprint === "string" ? manifest.sourceFingerprint : null;
33
- } catch {
34
- return null;
35
- }
36
- }
37
- export {
38
- ensureExtensionHarnessFresh
39
- };
@@ -1,91 +0,0 @@
1
- import path from "node:path";
2
- import { color } from "../cli-color.js";
3
- import { importRecipeHarnessCli } from "../paths.js";
4
- import { EXIT } from "./shared.js";
5
- import { parseArgs, usageError } from "./parse-args.js";
6
- import { resolveMetaMaskLibrarySources } from "./run-engine.js";
7
- const FLOWS_SUBCOMMANDS = ["list", "describe", "promote"];
8
- function stripFrontOnlyFlags(argv) {
9
- const out = [];
10
- for (let i = 0; i < argv.length; i += 1) {
11
- const arg = argv[i];
12
- if (arg === "--target" || arg === "--library") {
13
- i += 1;
14
- continue;
15
- }
16
- if (arg.startsWith("--target=") || arg.startsWith("--library=")) continue;
17
- out.push(arg);
18
- }
19
- return out;
20
- }
21
- function serializeLibrarySources(sources) {
22
- return sources.map((source) => source.name ? `${source.name}=${source.root}` : source.root).join(":");
23
- }
24
- function flowSourceTier(source, index, total) {
25
- if (index === total - 1 && source.name === "metamask") return "canonical";
26
- if (source.name === "personal") return "personal";
27
- return "team";
28
- }
29
- function restoreEnv(key, value) {
30
- if (value === void 0) delete process.env[key];
31
- else process.env[key] = value;
32
- }
33
- function libraryEntries(argv) {
34
- const entries = [];
35
- for (let index = 0; index < argv.length; index += 1) {
36
- const argument = argv[index];
37
- if (argument === "--library") {
38
- const value = argv[index + 1];
39
- if (value !== void 0) entries.push(value);
40
- index += 1;
41
- } else if (argument.startsWith("--library=")) {
42
- entries.push(argument.slice("--library=".length));
43
- }
44
- }
45
- return entries;
46
- }
47
- async function handleFlows(argv) {
48
- const { positional } = parseArgs(argv);
49
- const subcommand = positional[0];
50
- if (subcommand === "describe" && !positional[1]) {
51
- throw usageError("flows describe requires <ref>. Next: mm-harness flows list");
52
- }
53
- if (subcommand !== "describe" && positional[1]) {
54
- throw usageError(`flows ${subcommand ?? "list"} does not accept positional ${positional[1]}.`);
55
- }
56
- const configuredLibraries = libraryEntries(argv);
57
- const sources = await resolveMetaMaskLibrarySources(configuredLibraries);
58
- if (!sources) {
59
- throw usageError(
60
- "flows requires @farmslot/recipe-harness >= 0.3.3 (recipe-library support; flows resolve personal > team > canonical). Next: update the dependency and run yarn install."
61
- );
62
- }
63
- const forwarded = stripFrontOnlyFlags(argv);
64
- const jsonMode = forwarded.includes("--json");
65
- if (!jsonMode) {
66
- console.error(color("label", "flows resolve by precedence (highest wins; local shadows canonical):"));
67
- sources.forEach((source, index) => {
68
- const name = source.name ?? path.basename(source.root);
69
- console.error(` ${index + 1}. ${color("cmd", name)} ${color("dim", `[${flowSourceTier(source, index, sources.length)}]`)}`);
70
- });
71
- }
72
- const resolvedSubcommand = subcommand && FLOWS_SUBCOMMANDS.includes(subcommand) ? subcommand : "list";
73
- const forwardedSubcommandIndex = forwarded.indexOf(resolvedSubcommand);
74
- const delegatedArgs = forwardedSubcommandIndex === -1 ? forwarded : forwarded.filter((_, index) => index !== forwardedSubcommandIndex);
75
- const withSubcommand = [resolvedSubcommand, ...delegatedArgs];
76
- const { runRecipeHarnessCli } = await importRecipeHarnessCli();
77
- const previousLibraryPath = process.env.RECIPE_LIBRARY_PATH;
78
- const previousExitCode = process.exitCode;
79
- process.env.RECIPE_LIBRARY_PATH = serializeLibrarySources(sources);
80
- process.exitCode = void 0;
81
- try {
82
- await runRecipeHarnessCli(["flows", ...withSubcommand], { commandName: "mm-harness" });
83
- return typeof process.exitCode === "number" ? process.exitCode : EXIT.ok;
84
- } finally {
85
- restoreEnv("RECIPE_LIBRARY_PATH", previousLibraryPath);
86
- process.exitCode = previousExitCode;
87
- }
88
- }
89
- export {
90
- handleFlows
91
- };
@@ -1,64 +0,0 @@
1
- {
2
- "schema_version": 1,
3
- "kind": "recipe-flow-catalog",
4
- "owner": "metamask.perps",
5
- "flows": {
6
- "perps.clean_market_testnet": {
7
- "version": 1,
8
- "description": "Converge Perps to a clean testnet baseline before a proof window: wallet unlocked, requested market selected, no open positions or orders for that market.",
9
- "paramsSchema": {
10
- "type": "object",
11
- "required": ["market"],
12
- "properties": {
13
- "market": {
14
- "type": "string",
15
- "description": "Market symbol to converge, e.g. BTC."
16
- },
17
- "provider": {
18
- "type": "string",
19
- "description": "Optional Perps provider, e.g. hyperliquid; omit for the app default."
20
- }
21
- }
22
- },
23
- "requiresActions": ["metamask.wallet.ensure_unlocked", "metamask.perps.start_state"],
24
- "defaultPhase": "start_state",
25
- "defaultRecord": "trace_only",
26
- "postcondition": { "path": "$.status", "operator": "eq", "value": "pass" },
27
- "workflow": {
28
- "entry": "ensure-wallet",
29
- "nodes": {
30
- "ensure-wallet": {
31
- "action": "metamask.wallet.ensure_unlocked",
32
- "intent": "Ensure the wallet is unlocked before converging the Perps baseline",
33
- "next": "start-state"
34
- },
35
- "start-state": {
36
- "action": "metamask.perps.start_state",
37
- "intent": "Converge Perps to a clean {{params.market}} testnet baseline",
38
- "profile": "clean_market_testnet",
39
- "network": "testnet",
40
- "provider": "{{params.provider}}",
41
- "market": "{{params.market}}",
42
- "positions": { "state": "none", "mode": "matching" },
43
- "orders": { "state": "none", "mode": "matching" },
44
- "next": "done"
45
- },
46
- "done": { "action": "end", "status": "pass" }
47
- }
48
- },
49
- "examples": [
50
- {
51
- "description": "Clean BTC testnet baseline before a visual proof",
52
- "node": {
53
- "action": "call",
54
- "ref": "perps.clean_market_testnet",
55
- "params": { "market": "BTC" },
56
- "phase": "start_state",
57
- "record": "trace_only",
58
- "intent": "Converge Perps to a clean BTC testnet baseline before proof"
59
- }
60
- }
61
- ]
62
- }
63
- }
64
- }