@deeeed/metamask-harness 0.2.0 → 0.3.1

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 (110) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +47 -17
  3. package/{orchestration → adapters}/core/cleanup.sh +2 -2
  4. package/{orchestration → adapters}/core/inject.sh +8 -8
  5. package/{orchestration → adapters}/extension/cleanup.mjs +1 -1
  6. package/{orchestration → adapters}/extension/console-tail.mjs +3 -0
  7. package/{orchestration → adapters}/extension/ensure-browser.sh +4 -4
  8. package/{orchestration → adapters}/extension/inject.mjs +27 -31
  9. package/{orchestration → adapters}/extension/launch.sh +4 -4
  10. package/{orchestration → adapters}/extension/live.sh +5 -5
  11. package/{orchestration → adapters}/extension/readiness.mjs +12 -2
  12. package/{orchestration → adapters}/extension/refresh-build.sh +1 -1
  13. package/{orchestration → adapters}/extension/seed-fixture.sh +1 -1
  14. package/{orchestration → adapters}/extension/start-watch.sh +7 -7
  15. package/{runner → adapters}/extension/verify.sh +11 -11
  16. package/{orchestration → adapters}/extension/wallet-fixture-state.cjs +2 -2
  17. package/{orchestration → adapters}/manifest.json +96 -96
  18. package/{library/actions → adapters}/mobile/bridge-runtime/cdp-bridge.cjs +5 -5
  19. package/{orchestration → adapters}/mobile/cleanup.sh +3 -3
  20. package/{orchestration → adapters}/mobile/inject.sh +22 -24
  21. package/adapters/mobile/open-device.sh +260 -0
  22. package/{orchestration/compat-overlays → adapters/mobile/overlay/compat}/README.md +13 -4
  23. package/adapters/mobile/prewarm-bundle.sh +95 -0
  24. package/adapters/mobile/start-metro.sh +228 -0
  25. package/{runner → adapters}/mobile/verify.sh +6 -6
  26. package/adapters/mobile/wait-for-bridge.sh +108 -0
  27. package/adapters/mobile/yarn-setup.sh +20 -0
  28. package/{orchestration/lib → adapters/shared}/cli-ux.sh +5 -9
  29. package/{orchestration/lib → adapters/shared}/log-tui.mjs +57 -1
  30. package/{orchestration/lib → adapters/shared}/open-debug.mjs +32 -10
  31. package/{orchestration/lib → adapters/shared}/resolve-farmslot-ports.sh +47 -0
  32. package/adapters/shared/sync-wallet-fixture.sh +78 -0
  33. package/bin/mm-harness +37 -6
  34. package/docs/CLI-SPEC.md +41 -2
  35. package/docs/architecture.md +69 -38
  36. package/docs/runtime-file-conventions.md +3 -3
  37. package/library/actions/core/perps/_controller.mjs +1 -1
  38. package/library/actions/extension/platform/cdp.mjs +2 -2
  39. package/library/actions/extension/wallet/ensure_unlocked.mjs +1 -1
  40. package/library/actions/mobile/platform/bridge.mjs +1 -1
  41. package/library/actions/mobile/wallet/ensure_unlocked.mjs +1 -1
  42. package/library/actions/mobile/wallet/setup.mjs +2 -2
  43. package/library/library.json +1 -1
  44. package/package.json +8 -8
  45. package/{orchestration → src/adapters}/extension/runtime-decision.ts +5 -5
  46. package/{orchestration → src/adapters}/extension/runtime.ts +1 -1
  47. package/src/adapters/mobile/prepare.ts +210 -0
  48. package/{orchestration → src/adapters}/mobile/runtime-decision.ts +105 -5
  49. package/{runner/src → src}/adapters.ts +3 -3
  50. package/{orchestration/lib/cli-color.mjs → src/cli-color.ts} +21 -13
  51. package/{orchestration/lib/cli-commands.mjs → src/cli-commands.ts} +52 -45
  52. package/{orchestration/lib/cli-version.mjs → src/cli-version.ts} +35 -17
  53. package/{runner/src → src}/cli.ts +48 -40
  54. package/src/commands/debug.ts +63 -0
  55. package/src/commands/fixtures.ts +193 -0
  56. package/{runner/src → src}/commands/launch.ts +200 -54
  57. package/src/commands/logs.ts +83 -0
  58. package/{runner/src → src}/commands/shared.ts +40 -30
  59. package/src/commands/update.ts +316 -0
  60. package/{runner/src → src}/harness.ts +119 -16
  61. package/{runner/src → src}/heal-bounds.ts +20 -1
  62. package/src/index.ts +15 -0
  63. package/{runner/src → src}/live-adapter-contract.ts +1 -1
  64. package/{runner/src → src}/mm-harness-cli.ts +64 -3
  65. package/{runner/src → src}/paths.ts +2 -2
  66. package/{orchestration/lib/progress.mjs → src/progress.ts} +31 -21
  67. package/docs/extension-runtime-commands.md +0 -60
  68. package/docs/harness-cli.md +0 -43
  69. package/orchestration/compat-overlays/mobile/README.md +0 -13
  70. package/orchestration/doctor.mjs +0 -72
  71. package/orchestration/lib/cli-home.mjs +0 -354
  72. package/orchestration/lib/recipe-paths.mjs +0 -26
  73. package/orchestration/mobile/launch.sh +0 -137
  74. package/orchestration/mobile/live.sh +0 -125
  75. package/orchestration/porcelain/metamask-recipe +0 -99
  76. package/orchestration/porcelain/mm-recipe +0 -1591
  77. package/orchestration/porcelain/mme-recipe +0 -1181
  78. package/runner/src/commands/debug.ts +0 -44
  79. package/runner/src/commands/fixtures.ts +0 -99
  80. package/runner/src/commands/logs.ts +0 -60
  81. package/runner/src/index.ts +0 -6
  82. /package/{orchestration → adapters}/extension/launch-browser.cjs +0 -0
  83. /package/{orchestration → adapters}/extension/pin-remote-flags.cjs +0 -0
  84. /package/{orchestration → adapters}/extension/sidepanel-toggle.sh +0 -0
  85. /package/{orchestration → adapters}/extension/snapshot-dist.sh +0 -0
  86. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -0
  87. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/config.cjs +0 -0
  88. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/issue-capture.cjs +0 -0
  89. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/target-discovery.cjs +0 -0
  90. /package/{library/actions → adapters}/mobile/bridge-runtime/lib/ws-client.cjs +0 -0
  91. /package/{library/actions → adapters}/mobile/bridge-runtime/setup-wallet.sh +0 -0
  92. /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -0
  93. /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -0
  94. /package/{orchestration/compat-overlays/mobile → adapters/mobile/overlay/compat}/rn81-message-event-source.patch +0 -0
  95. /package/{orchestration/lib → adapters/shared}/activate-repo-node.sh +0 -0
  96. /package/{orchestration/lib → adapters/shared}/ensure-runner-deps.sh +0 -0
  97. /package/{orchestration/lib → adapters/shared}/harness-path.sh +0 -0
  98. /package/{orchestration/lib → adapters/shared}/hash-helpers.sh +0 -0
  99. /package/{orchestration/lib → adapters/shared}/json-field.sh +0 -0
  100. /package/{orchestration/lib → adapters/shared}/path-defaults.json +0 -0
  101. /package/{orchestration → src/adapters}/extension/ensure-ready.ts +0 -0
  102. /package/{orchestration → src/adapters}/extension/extension-id.ts +0 -0
  103. /package/{orchestration → src/adapters}/mobile/deps-markers.ts +0 -0
  104. /package/{runner/src → src}/completions-cache.ts +0 -0
  105. /package/{runner/src → src}/doctor.ts +0 -0
  106. /package/{runner/src → src}/manifest.ts +0 -0
  107. /package/{runner/src → src}/recording-target.ts +0 -0
  108. /package/{runner/src → src}/run-recording.ts +0 -0
  109. /package/{runner/src → src}/runner.ts +0 -0
  110. /package/{runner/src → src}/types.ts +0 -0
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bash
2
+ # sync-wallet-fixture.sh — copy the wallet fixture to the canonical runtime slot.
3
+ #
4
+ # Purpose:
5
+ # After a successful overlay install, ensures the canonical wallet fixture is
6
+ # present in the checkout's runtime dir. Non-zero exit is informational (no
7
+ # fixture source found); the install already succeeded, so the overlay is fresh.
8
+ #
9
+ # Inputs (flags / env):
10
+ # --target <checkout> (default $PWD)
11
+ # --slot-id <id> (env FARMSLOT_SLOT_ID, SLOT_ID — farmslot sync source)
12
+ # --cdp-port <port> (env CDP_PORT — extension fixture seeding port)
13
+ #
14
+ # Outputs:
15
+ # Copies wallet-fixture.json to <target>/<RECIPE_RUNTIME_DIR>/wallet-fixture.json
16
+ # when a source is found. Exit 0 always (non-zero only when no source found;
17
+ # callers treat this as informational).
18
+ #
19
+ # OS-glue only: file copy and farmslot delegation; decision logic lives in
20
+ # src/commands/fixtures.ts.
21
+ set -uo pipefail
22
+
23
+ TARGET="$PWD"
24
+ SLOT_ID="${FARMSLOT_SLOT_ID:-${SLOT_ID:-}}"
25
+ CDP_PORT="${CDP_PORT:-}"
26
+
27
+ require_value() { [ "$#" -ge 2 ] || { printf 'Missing value for %s\n' "$1" >&2; exit 2; }; }
28
+
29
+ while [ "$#" -gt 0 ]; do
30
+ case "$1" in
31
+ --target) require_value "$@"; TARGET="$2"; shift 2 ;;
32
+ --slot-id) require_value "$@"; SLOT_ID="$2"; shift 2 ;;
33
+ --cdp-port) require_value "$@"; CDP_PORT="$2"; shift 2 ;;
34
+ -h|--help)
35
+ printf 'Usage: sync-wallet-fixture.sh [--target <dir>] [--slot-id <id>] [--cdp-port <port>]\n'
36
+ exit 0
37
+ ;;
38
+ *) printf 'Unknown arg: %s\n' "$1" >&2; exit 2 ;;
39
+ esac
40
+ done
41
+
42
+ TARGET="$(cd "$TARGET" && pwd)"
43
+ RUNTIME_DIR="${RECIPE_RUNTIME_DIR:-temp/recipe/runtime}"
44
+ CANONICAL="$TARGET/$RUNTIME_DIR/wallet-fixture.json"
45
+
46
+ mkdir -p "$(dirname "$CANONICAL")"
47
+
48
+ # Already present — nothing to do.
49
+ if [ -f "$CANONICAL" ]; then
50
+ exit 0
51
+ fi
52
+
53
+ # RECIPE_WALLET_FIXTURE env points at a fixture directly.
54
+ if [ -n "${RECIPE_WALLET_FIXTURE:-}" ] && [ -f "${RECIPE_WALLET_FIXTURE}" ]; then
55
+ cp "${RECIPE_WALLET_FIXTURE}" "$CANONICAL"
56
+ chmod 600 "$CANONICAL" 2>/dev/null || true
57
+ printf 'sync-wallet-fixture: linked wallet-fixture.json from RECIPE_WALLET_FIXTURE\n' >&2
58
+ exit 0
59
+ fi
60
+
61
+ # Farmslot slot sync (requires farmslot root and sync-fixtures.sh).
62
+ if [ -n "$SLOT_ID" ]; then
63
+ FARMSLOT_ROOT="${FARMSLOT_ROOT:-}"
64
+ for candidate in "$HOME/dev/farmslot" "$HOME/farmslot"; do
65
+ [ -d "$candidate/scripts" ] || continue
66
+ FARMSLOT_ROOT="$candidate"
67
+ break
68
+ done
69
+ if [ -n "$FARMSLOT_ROOT" ] && [ -f "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" ]; then
70
+ if bash "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" --slot "$SLOT_ID" 2>/dev/null; then
71
+ [ -f "$CANONICAL" ] && exit 0
72
+ fi
73
+ fi
74
+ fi
75
+
76
+ # No fixture source found — informational non-zero (callers tolerate this).
77
+ printf 'sync-wallet-fixture: no wallet fixture source found for %s\n' "$TARGET" >&2
78
+ exit 1
package/bin/mm-harness CHANGED
@@ -1,10 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  # mm-harness — the single front door for the MetaMask recipe loop.
3
3
  #
4
- # Same node/tsx resolution mechanism as bin/metamask-recipe, but the entry is the
5
- # commander-based CLI (runner/src/mm-harness-cli.ts) that presents only the
6
- # validated end-state surface. Legacy names (metamask-recipe/mm-recipe/mme-recipe,
7
- # manifest/live/runtime-*) stay reachable via their own bins until the caller sweep.
4
+ # The entry is the commander-based CLI (src/mm-harness-cli.ts) that presents the
5
+ # end-state surface; it resolves node/tsx/dist the same way whether the package is
6
+ # installed as a dependency or run from a source checkout.
8
7
  set -euo pipefail
9
8
  SOURCE="${BASH_SOURCE[0]}"
10
9
  while [ -L "$SOURCE" ]; do
@@ -19,13 +18,45 @@ done
19
18
  SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
20
19
  RUNNER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
21
20
 
22
- ENSURE_DEPS="$RUNNER_DIR/orchestration/lib/ensure-runner-deps.sh"
21
+ # Dev/prod switch (the single override point). MM_HARNESS_BIN, when set, is the
22
+ # authoritative path to a dev checkout's bin/mm-harness with active pre-release
23
+ # changes; unset resolves the installed/global mm-harness (prod). Handing the
24
+ # invocation over here, before any dependency work, lets the dev checkout fully
25
+ # own the run. The self-path guard prevents an exec loop when MM_HARNESS_BIN
26
+ # already points at this script — including via a symlink (both sides are resolved
27
+ # to their real paths before comparing).
28
+ if [ -n "${MM_HARNESS_BIN:-}" ]; then
29
+ self_real="$SCRIPT_DIR/$(basename "$SOURCE")"
30
+ # Resolve MM_HARNESS_BIN through any symlink chain so a symlink-to-self is caught.
31
+ mm_bin_src="$MM_HARNESS_BIN"
32
+ while [ -L "$mm_bin_src" ]; do
33
+ mm_bin_dir="$(cd "$(dirname "$mm_bin_src")" && pwd -P)"
34
+ mm_bin_target="$(readlink "$mm_bin_src")"
35
+ if [[ "$mm_bin_target" == /* ]]; then
36
+ mm_bin_src="$mm_bin_target"
37
+ else
38
+ mm_bin_src="$mm_bin_dir/$mm_bin_target"
39
+ fi
40
+ done
41
+ bin_real_dir="$(cd "$(dirname "$mm_bin_src")" 2>/dev/null && pwd -P || true)"
42
+ bin_real="${bin_real_dir:+$bin_real_dir/$(basename "$mm_bin_src")}"
43
+ if [ "$bin_real" != "$self_real" ]; then
44
+ if [ ! -x "$MM_HARNESS_BIN" ]; then
45
+ echo "mm-harness: MM_HARNESS_BIN is not an executable: $MM_HARNESS_BIN" >&2
46
+ echo " Next: point MM_HARNESS_BIN at a checkout's bin/mm-harness, or unset it to use the global install" >&2
47
+ exit 1
48
+ fi
49
+ exec "$MM_HARNESS_BIN" "$@"
50
+ fi
51
+ fi
52
+
53
+ ENSURE_DEPS="$RUNNER_DIR/adapters/shared/ensure-runner-deps.sh"
23
54
  if [ -f "$ENSURE_DEPS" ]; then
24
55
  # shellcheck disable=SC1090
25
56
  bash "$ENSURE_DEPS" "$RUNNER_DIR"
26
57
  fi
27
58
 
28
- ENTRY_TS="$RUNNER_DIR/runner/src/mm-harness-cli.ts"
59
+ ENTRY_TS="$RUNNER_DIR/src/mm-harness-cli.ts"
29
60
  ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"
30
61
 
31
62
  find_protocol_root() {
package/docs/CLI-SPEC.md CHANGED
@@ -36,8 +36,8 @@ exhaustive —
36
36
  - **REMOVE** — name/bin/subcommand deleted in the wave; **requires a one-line justification** (needs Arthur's sign-off).
37
37
 
38
38
  **No row may be unmapped.** An unmapped row means the refactor is not allowed to
39
- proceed. Source of truth: the bins' `case` statements + the naming audit in
40
- [harness-cli.md](harness-cli.md). Every row is real.
39
+ proceed. Source of truth: the bins' `case` statements + the naming audit.
40
+ Every row is real.
41
41
 
42
42
  ## Surface A — `metamask-recipe` typed CLI (`runner/src/cli.ts`)
43
43
 
@@ -913,3 +913,42 @@ The fleet is just another agent-grade caller:
913
913
  slot's prepare hook calls the same verb with slot context; dispatch scales it
914
914
  across the fleet.
915
915
 
916
+ ## Appendix — Extension runtime lifecycle (internal notes)
917
+
918
+ Internal implementation notes for the Extension runtime lifecycle — the
919
+ self-healing path that gets a checkout from "installed" to "a live, verified
920
+ extension under CDP control". These are **not** a public command surface: the
921
+ probes below are internal functions, composed for you by `launch --verify` and by
922
+ the readiness commands (`doctor --fix`, `verify`). The runner is the source of
923
+ truth for Extension harness injection, readiness decisions, extension-id
924
+ resolution, and live CDP health checks.
925
+
926
+ Public entry points:
927
+
928
+ - **`install`** writes the Extension runtime overlay under the configured harness
929
+ root. CI/agents run it explicitly; the daily-loop commands auto-ensure it.
930
+ - **`verify`** confirms the overlay is present and healthy — a live CDP health
931
+ probe when a runtime is reachable, no launch.
932
+ - **`launch --verify`** installs the overlay if missing, launches/attaches the
933
+ extension, and polls CDP until the runtime is ready.
934
+ - **`doctor` / `doctor --fix`** report readiness and (with `--fix`) run the
935
+ healing steps below WITHOUT starting the app.
936
+
937
+ Internal lifecycle stages, composed in order (each idempotent, each reporting the
938
+ cheapest next action so the path converges rather than rebuilding from scratch):
939
+
940
+ - **Extension-id resolution** — resolves the deterministic unpacked extension id
941
+ from `dist/chrome/manifest.json`, optionally verified against a live CDP target.
942
+ Runs first; every later stage keys off this id.
943
+ - **Readiness decision** — inspects the checkout + live browser and returns the
944
+ cheapest next action (`install`, `build`, `relaunch`, or `ready`) plus the
945
+ concrete `actions[]` to execute.
946
+ - **Runtime launch** — launches Chrome with the installed harness helper, seeds
947
+ the wallet fixture, and runs live smoke verification. Two tiers: reuse the
948
+ existing dist (quick relaunch), or the clean-build path (clear webpack cache,
949
+ start the harness-owned watcher, wait for a clean compile, then launch + verify).
950
+ - **Convergence** — converges the live browser to exactly one healthy `home.html`
951
+ tab (closing strays, reopening if needed) and confirms it with the health probe.
952
+ - **Health probe** — read-only liveness probe for the running extension over CDP;
953
+ the final gate of convergence and the standalone answer for `verify`.
954
+
@@ -1,5 +1,40 @@
1
1
  # MetaMask Recipe Runner Architecture
2
2
 
3
+ **Control flows DOWN only — a DAG, never a loop.**
4
+
5
+ ```
6
+ bin/mm-harness ← entry (thin bootstrap)
7
+
8
+
9
+ ┌───────────────────────────────────────────────┐
10
+ │ src/ THE BRAIN — all TypeScript │
11
+ │ CLI · decisions · state machines · run │
12
+ │ engine · healing/errors · evidence │
13
+ └───────────────────────────────────────────────┘
14
+ │ spawns │ reads
15
+ ▼ ▼
16
+ adapters/<platform>/ library/
17
+ DEVICE LEAVES (shell) VOCABULARY (data)
18
+ one op · args in · recipes · actions ·
19
+ result out · exit flows — pure JSON/mjs
20
+ (yarn-setup, start-metro,
21
+ open-device, xcrun, adb,
22
+ webpack, chrome)
23
+ ```
24
+
25
+ | dir | is | rule |
26
+ |---|---|---|
27
+ | `bin/` | entry | one thin bootstrap |
28
+ | `src/` | the brain | ALL TypeScript, ALL decisions/sequencing |
29
+ | `adapters/<platform>/` | device leaves | one op each · never orchestrates · **never calls `mm-harness`** |
30
+ | `library/` | vocabulary | pure recipe/action/flow data |
31
+
32
+ **The one rule everything follows:** nothing under `adapters/` or `library/` ever calls `mm-harness`. TS orchestrates and spawns leaves; leaves do one device op and exit; vocabulary is data. (External callers — farm hooks, skills, overlay delegate — invoking `mm-harness` at the top level is fine; the ban is on the harness's OWN scripts calling back in.)
33
+
34
+ **Why:** every past mess (half-wired porcelain, commands referenced-but-unbuilt, stub tests faking the thing under test) came from control looping back up. Down-only makes that class of bug unrepresentable — and means less code.
35
+
36
+ ---
37
+
3
38
  This repo is the MetaMask-specific adapter layer for Recipe Protocol v1. It is
4
39
  not the protocol engine, not a skills repo, not a slot farm, and not product
5
40
  code. Its job is to publish reviewable MetaMask capabilities and run them
@@ -21,9 +56,9 @@ doing more than parsing flags and calling `mm-harness`, that logic
21
56
  belongs in this runner instead. Concrete example in flight: the
22
57
  `recipe-harness` skill currently locates and execs runner internals in
23
58
  hundreds of lines of bash (resolve runner source, dispatch to
24
- `orchestration/<platform>/{inject,cleanup}.{sh,mjs}` — `.sh` for Mobile/Core,
59
+ `adapters/<platform>/{inject,cleanup}.{sh,mjs}` — `.sh` for Mobile/Core,
25
60
  `.mjs` for Extension); the fix is a
26
- [`mm-harness <install|verify|cleanup|live>`](harness-cli.md) command here, so the
61
+ [`mm-harness <install|verify|cleanup|live>`](CLI-SPEC.md) command here, so the
27
62
  skill thins to a few lines that call it. Same pattern applies to every other
28
63
  `recipe-*` skill over time.
29
64
 
@@ -32,10 +67,10 @@ skill thins to a few lines that call it. Same pattern applies to every other
32
67
 
33
68
  | Extension | Where | Use for | Why |
34
69
  |---|---|---|---|
35
- | `.ts` | `runner/src/**` | CLI parsing, manifests, adapter binding, runtime decisions — typed/testable/shared logic | Compiled and typed; the maintainable core that everything else should grow into |
36
- | `.mjs` | `library/actions/**`, `orchestration/**`, `scripts/**` | Standalone action adapters and injected runtime helpers | Run-anywhere with zero build step, whether invoked from a published package or a raw checkout |
70
+ | `.ts` | `src/**` | CLI parsing, manifests, adapter binding, runtime decisions — typed/testable/shared logic | Compiled and typed; the maintainable core that everything else should grow into |
71
+ | `.mjs` | `library/actions/**`, `adapters/**`, `scripts/**` | Standalone action adapters and injected runtime helpers | Run-anywhere with zero build step, whether invoked from a published package or a raw checkout |
37
72
  | `.cjs` | scattered, quarantined | Compatibility islands: portable `require()` from shell, bridge code for older runtime contexts | Only when the runtime context makes ESM brittle — not a default choice |
38
- | `.sh` | `bin/`, `orchestration/{mobile,extension,core}/`, `runner/{mobile,extension}/`, `orchestration/lib/` | Thin OS/device glue at the edge: `simctl`, `adb`, Chrome process flags, git-exclude, filesystem setup | Must run before/without Node business logic; never recipe graph execution or MetaMask domain semantics |
73
+ | `.sh` | `bin/`, `adapters/{mobile,extension,core}/`, `adapters/shared/` | Thin OS/device glue at the edge: `simctl`, `adb`, Chrome process flags, git-exclude, filesystem setup | Must run before/without Node business logic; never recipe graph execution or MetaMask domain semantics |
39
74
 
40
75
  The mix is bash-heavy today because sandbox setup crosses OS/device boundaries
41
76
  (see "Why there are shell scripts" below) — that is current reality, not the
@@ -85,15 +120,15 @@ summary.json · trace.json · artifact-manifest.json · screenshots/logs
85
120
 
86
121
  **Dependency direction is one-way and provable.** The runner *depends on*
87
122
  `@farmslot/recipe-harness` + `@farmslot/protocol` (see `package.json`), and
88
- `runner/src/**` *imports* the generic contracts rather than forking them:
123
+ `src/**` *imports* the generic contracts rather than forking them:
89
124
 
90
- - `runner/src/cli.ts` imports `RecipeRunResult` from `@farmslot/recipe-harness`
125
+ - `src/cli.ts` imports `RecipeRunResult` from `@farmslot/recipe-harness`
91
126
  and delegates library `flows` to the harness CLI (`importRecipeHarnessCli`).
92
- - `runner/src/runner.ts` wires `metamask.*` adapters INTO the farmslot
127
+ - `src/runner.ts` wires `metamask.*` adapters INTO the farmslot
93
128
  `RecipeRunner`; it does not implement graph execution.
94
- - `runner/src/manifest.ts` loads *MetaMask* manifests typed by the *farmslot*
129
+ - `src/manifest.ts` loads *MetaMask* manifests typed by the *farmslot*
95
130
  protocol document type.
96
- - `runner/src/recording-target.ts` is a MetaMask provider over the farmslot
131
+ - `src/recording-target.ts` is a MetaMask provider over the farmslot
97
132
  `RecordingTarget` contract.
98
133
 
99
134
  **The invariant both repos assert:** farmslot stays MetaMask-free; the runner
@@ -104,7 +139,7 @@ generic (farmslot); encodes MetaMask app knowledge = runner.
104
139
  > Note on naming: "harness" refers to the tool (`mm-harness`) and the generic
105
140
  > engine package (`@farmslot/recipe-harness`). The per-checkout files the tool
106
141
  > installs into a target are the **runtime overlay** (`install`/`verify`/
107
- > `cleanup`/`live`), never "the harness" — see [harness-cli.md](harness-cli.md).
142
+ > `cleanup`/`live`), never "the harness" — see [CLI-SPEC.md](CLI-SPEC.md).
108
143
 
109
144
  ## One-minute model
110
145
 
@@ -141,8 +176,8 @@ is the main way to understand the repository.
141
176
 
142
177
  | Subsystem | Question it answers | Primary files | Should contain | Should not contain |
143
178
  |---|---|---|---|---|
144
- | Recipe capability/execution | “What can a MetaMask recipe do, and how does a node execute?” | `library/manifests/`, `library/recipes/`, `runner/src/runner.ts`, `runner/src/adapters.ts`, `runner/src/live-adapter-contract.ts`, `library/actions/` | action manifests, domain actions, UI transport binding, adapter outputs, proof semantics | Metro startup, Chrome process flags, simulator boot, git-exclude/rsync cleanup |
145
- | Runtime lifecycle / sandbox helpers | “How do I give an agent an isolated app session that is ready to inspect or run recipes?” | `bin/mm-recipe`, `bin/mme-recipe`, `orchestration/{mobile,extension,core}/`, `runner/{mobile,extension}/`, `orchestration/lib/` | install/sync harness, start/reuse Metro or Chrome, prewarm bundles, open Extension full-screen or popup-style, prepare dedicated profiles/fixtures, check build/runtime health, cleanup local files | new recipe schema, graph traversal, MetaMask business semantics, task-specific proof logic |
179
+ | Recipe capability/execution | “What can a MetaMask recipe do, and how does a node execute?” | `library/manifests/`, `library/recipes/`, `src/runner.ts`, `src/adapters.ts`, `src/live-adapter-contract.ts`, `library/actions/` | action manifests, domain actions, UI transport binding, adapter outputs, proof semantics | Metro startup, Chrome process flags, simulator boot, git-exclude/rsync cleanup |
180
+ | Runtime lifecycle / sandbox helpers | “How do I give an agent an isolated app session that is ready to inspect or run recipes?” | `adapters/porcelain/`, `adapters/{mobile,extension,core}/`, `adapters/shared/` | install/sync harness, start/reuse Metro or Chrome, prewarm bundles, open Extension full-screen or popup-style, prepare dedicated profiles/fixtures, check build/runtime health, cleanup local files | new recipe schema, graph traversal, MetaMask business semantics, task-specific proof logic |
146
181
 
147
182
  When reviewing a change, first decide which subsystem it touches. Recipe changes
148
183
  should be validated against manifests and action artifacts. Runtime lifecycle changes should be validated by install/launch/live/verify
@@ -168,29 +203,26 @@ describes **how an agent should work**, it belongs in skills.
168
203
 
169
204
  | Path | Responsibility |
170
205
  |---|---|
171
- | `bin/metamask-recipe` | Public binary. Dispatches to typed CLI and platform convenience commands. |
172
- | `bin/mm-recipe` | Mobile convenience/runtime UX: start/reuse Metro, prewarm bundle, launch app, query bridge, setup wallet, screenshot. |
173
- | `bin/mme-recipe` | Extension convenience/runtime UX: install, health, decision, ready, watch/refresh/reopen, run recipes. |
174
- | `runner/src/cli.ts` | Typed command handlers: manifests, actions, doctor, runtime health/decision/launch, `run`, self-test. |
175
- | `runner/src/runner.ts` | Creates the Recipe runner by combining shared core/ui adapters with MetaMask live adapters. Enables the Recipe HUD metadata. |
176
- | `runner/src/adapters.ts` | MetaMask adapter binding and `ui.*` transport selection for Mobile vs Extension. Refuses static placeholders for live-only proof actions. |
177
- | `runner/src/live-adapter-contract.ts` | Script adapter contract and lookup rules for `library/actions/<platform>/<domain>/*.mjs`. |
206
+ | `src/cli.ts` | Typed command handlers: manifests, actions, doctor, runtime health/decision/launch, `run`. |
207
+ | `src/runner.ts` | Creates the Recipe runner by combining shared core/ui adapters with MetaMask live adapters. Enables the Recipe HUD metadata. |
208
+ | `src/adapters.ts` | MetaMask adapter binding and `ui.*` transport selection for Mobile vs Extension. Refuses static placeholders for live-only proof actions. |
209
+ | `src/live-adapter-contract.ts` | Script adapter contract and lookup rules for `library/actions/<platform>/<domain>/*.mjs`. |
178
210
  | `library/manifests/*.action-manifest.json` | Reviewable capability contract. A recipe may only call declared actions. |
179
211
  | `library/actions/mobile/` | Mobile action implementations. Talks to the runner bridge and app-exposed `globalThis.__AGENTIC__` hooks. |
180
212
  | `library/actions/extension/` | Extension action implementations. Talks to Chrome/extension pages over CDP. |
181
- | `orchestration/mobile/inject.sh` | Installs/syncs the Mobile runtime overlay under the configured harness root and protects cleanup/git-exclude behavior. |
182
- | `orchestration/extension/inject.mjs` | Installs/syncs Extension runtime helpers under the configured harness root. |
183
- | `orchestration/mobile/` + `runner/mobile/` | Runner-owned Mobile launch/live/inject/cleanup and verify helpers copied into installed harnesses. |
184
- | `orchestration/extension/` + `runner/extension/` | Runner-owned Extension launch/live/watch/windows/wallet-state/readiness helpers (orchestration) and verify (recipe) copied into installed harnesses. |
185
- | `orchestration/lib/path-defaults.json` | Single source for default `recipeHarnessRoot` and `recipeRuntimeDir`. |
186
- | `orchestration/lib/harness-path.sh`, `orchestration/lib/recipe-paths.mjs`, `runner/src/paths.ts` | Shell, standalone Node, and TypeScript accessors for those defaults plus validation. |
213
+ | `adapters/mobile/inject.sh` | Installs/syncs the Mobile runtime overlay under the configured harness root and protects cleanup/git-exclude behavior. |
214
+ | `adapters/extension/inject.mjs` | Installs/syncs Extension runtime helpers under the configured harness root. |
215
+ | `adapters/mobile/` | Runner-owned Mobile launch/live/inject/cleanup and verify helpers copied into installed harnesses. |
216
+ | `adapters/extension/` | Runner-owned Extension launch/live/watch/windows/wallet-state/readiness helpers (orchestration) and verify (recipe) copied into installed harnesses. |
217
+ | `adapters/shared/path-defaults.json` | Single source for default `recipeHarnessRoot` and `recipeRuntimeDir`. |
218
+ | `adapters/shared/harness-path.sh`, `src/paths.ts` | Shell and TypeScript accessors for those defaults plus validation. |
187
219
  | `library/recipes/` | Reusable smoke/action-validation recipes only. Task-specific proof recipes stay task-local. |
188
220
  | `library/library.json`, `library/flows/` | Canonical recipe library: minimal, stable flows recipes can `call`. Personal/team libraries shadow it by default — see [Recipe Libraries](recipe-libraries.md). |
189
221
  | `docs/` | Runner architecture, contracts, and operational conventions. |
190
222
 
191
223
  ## Runtime paths and installed harnesses
192
224
 
193
- Defaults are centralized in `orchestration/lib/path-defaults.json`:
225
+ Defaults are centralized in `adapters/shared/path-defaults.json`:
194
226
 
195
227
  ```json
196
228
  {
@@ -210,7 +242,7 @@ Install commands write a small runtime package into the target checkout:
210
242
  <target>/<recipeHarnessRoot>/<adapter>/
211
243
  manifest.json installed source/revision/cleanup metadata
212
244
  action-manifest.json snapshot of the adapter manifest
213
- runner/bin/metamask-recipe delegate back to the resolved runner source
245
+ runner/bin/mm-harness delegate back to the resolved runner source
214
246
  runner/recipes/ reusable recipe snapshot
215
247
  scripts/ adapter runtime helpers copied from this repo
216
248
  ```
@@ -222,10 +254,10 @@ this runner.
222
254
  ## Recipe execution vs sandbox lifecycle
223
255
 
224
256
  `mm-harness run <recipe.json> --adapter ...` is the recipe path. It creates
225
- a shared Recipe runner (`runner/src/runner.ts`), validates the recipe against the
257
+ a shared Recipe runner (`src/runner.ts`), validates the recipe against the
226
258
  manifest, executes nodes, and writes artifacts. If a bug is about action fields,
227
259
  trace output, adapter semantics, or whether a recipe proves a claim, start in
228
- `library/manifests/`, `runner/src/`, `library/actions/`, and `library/recipes/`.
260
+ `library/manifests/`, `src/`, `library/actions/`, and `library/recipes/`.
229
261
 
230
262
  `prepare`, `launch`, `live`, `verify`, `status`, `decision`, and `ready` are
231
263
  sandbox lifecycle paths. They give the agent a reproducible local app session:
@@ -233,8 +265,7 @@ Mobile with Metro/dev-client/simulator and the bridge online; Extension with an
233
265
  isolated browser profile, unpacked extension loaded, and a known home/popup-style
234
266
  UI target. If a bug is about Metro, bundle prewarm, simulator launch, Chrome CDP,
235
267
  Extension full-screen vs popup presentation, build freshness, wallet fixture
236
- placement, git-exclude, or cleanup, start in `bin/mm-recipe`, `bin/mme-recipe`,
237
- `orchestration/{mobile,extension,core}/` and `runner/{mobile,extension}/`.
268
+ placement, git-exclude, or cleanup, start in `adapters/{mobile,extension,core}/`.
238
269
 
239
270
  Do not put recipe graph traversal into shell scripts. Shell scripts may prepare
240
271
  or inspect the sandboxed runtime, then delegate graph execution to
@@ -247,7 +278,7 @@ These names are easy to mix up; they are different concerns.
247
278
  | Term | What it is | Why it exists |
248
279
  |---|---|---|
249
280
  | Recipe HUD | A visual overlay driven by Recipe runner metadata (`intent`, current node, status). | Makes screenshots/videos explain what the recipe is doing without exposing secrets. |
250
- | Mobile bridge | Runner-side CDP/Hermes bridge process under `library/actions/mobile/bridge-runtime/`. | Lets the runner call app-exposed commands, read state, press UI targets, and capture status from React Native. |
281
+ | Mobile bridge | Runner-side CDP/Hermes bridge process under `adapters/mobile/bridge-runtime/`. | Lets the runner call app-exposed commands, read state, press UI targets, and capture status from React Native. |
251
282
  | `globalThis.__AGENTIC__` | Development-only in-app command surface exposed by the Mobile overlay/patch. | Gives the bridge a stable API for route/status/wallet/UI operations when the app lacks a built-in automation API. |
252
283
  | Extension CDP hooks | Chrome DevTools Protocol access to extension pages/background state hooks. | Lets the runner inspect/drive the unpacked Extension without patching product source. |
253
284
 
@@ -260,10 +291,10 @@ Mobile has the most moving parts because React Native does not expose a browser
260
291
  DOM by default.
261
292
 
262
293
  ```text
263
- mm-recipe / orchestration/mobile/*.sh
264
- starts/reuses Metro, prewarms bundle, launches iOS/Android dev client
294
+ mm-harness launch ios|android
295
+ prepareMobile() in-process yarn-setup / start-metro / open-device leaves
265
296
 
266
- library/actions/mobile/bridge-runtime/cdp-bridge.cjs
297
+ adapters/mobile/bridge-runtime/cdp-bridge.cjs
267
298
  │ connects to Hermes / RN debug runtime
268
299
 
269
300
  globalThis.__AGENTIC__ inside the app
@@ -295,7 +326,7 @@ Extension does not need an in-product source patch. The runner works through an
295
326
  unpacked `dist/chrome` build and Chrome CDP.
296
327
 
297
328
  ```text
298
- mme-recipe / orchestration/extension/*.sh
329
+ mme-recipe / adapters/extension/*.sh
299
330
  │ checks dist freshness, build health, fixture/profile state
300
331
 
301
332
  Chrome for Testing with --load-extension=<runtime-dist>
@@ -329,7 +360,7 @@ Allowed in shell:
329
360
  Not allowed in shell:
330
361
 
331
362
  - Recipe v1 graph execution;
332
- - MetaMask domain semantics that can live in `runner/src/**/*.ts` or `library/actions/**/*.mjs`;
363
+ - MetaMask domain semantics that can live in `src/**/*.ts` or `library/actions/**/*.mjs`;
333
364
  - duplicated action manifest logic;
334
365
  - product-specific business decisions beyond runtime boot/health checks.
335
366
 
@@ -6,9 +6,9 @@ package without requiring target app builds to transpile runner code.
6
6
 
7
7
  ## Extension rules
8
8
 
9
- - `runner/src/**/*.ts` — typed runner core: CLI parsing, manifests, adapter binding,
9
+ - `src/**/*.ts` — typed runner core: CLI parsing, manifests, adapter binding,
10
10
  runtime decisions, and shared helper logic.
11
- - `library/actions/**/*.mjs`, `orchestration/**/*.mjs`, and `scripts/**/*.mjs` —
11
+ - `library/actions/**/*.mjs`, `adapters/**/*.mjs`, and `scripts/**/*.mjs` —
12
12
  standalone ESM modules that Node executes directly with no build step. Use
13
13
  these for action adapters and small injected/runtime helpers.
14
14
  - `*.cjs` — compatibility islands only. Keep these quarantined for helper code
@@ -31,6 +31,6 @@ The mix is intentional only when the boundary is clear:
31
31
  4. Shell stays at the edge for host/device commands.
32
32
 
33
33
  If a file crosses those boundaries, move the logic inward: shell should call a
34
- Node module, standalone `.mjs` should become typed `runner/src/**/*.ts` when it grows
34
+ Node module, standalone `.mjs` should become typed `src/**/*.ts` when it grows
35
35
  shared domain logic, and new compatibility needs should be documented before
36
36
  adding another `.cjs` file.
@@ -8,7 +8,7 @@ import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
8
8
  // this adapter under the bundled tsx (see commandFor/importsSourceTypescript in
9
9
  // src/live-adapter-contract.ts). The core adapter dynamic-imports the perps
10
10
  // controller TypeScript at runtime, so it MUST run under tsx, not plain node.
11
- import { walletFixturePath } from '../../../../runner/src/paths.ts';
11
+ import { walletFixturePath } from '../../../../src/index.ts';
12
12
 
13
13
  // Shared headless instantiation for the MetaMask `core` adapter.
14
14
  //
@@ -10,8 +10,8 @@ import {
10
10
  extensionIdPath,
11
11
  recipeHarnessPath,
12
12
  walletFixturePath,
13
- } from '../../../../runner/src/paths.ts';
14
- import { captureActiveRecipeRecordingSnapshot } from '../../../../runner/src/run-recording.ts';
13
+ captureActiveRecipeRecordingSnapshot,
14
+ } from '../../../../src/index.ts';
15
15
 
16
16
  // Resolve the Farmslot harness through normal package dependencies by default.
17
17
  // Local Farmslot source is only a dev override handled by src/paths.ts.
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
3
- import { walletFixturePath } from '../../../../runner/src/paths.ts';
3
+ import { walletFixturePath } from '../../../../src/index.ts';
4
4
 
5
5
  async function fixturePassword(projectRoot) {
6
6
  const candidates = [
@@ -31,7 +31,7 @@ function bridgeScript(input) {
31
31
  }
32
32
 
33
33
  function runtimeDir() {
34
- return fileURLToPath(new URL('../bridge-runtime', import.meta.url));
34
+ return fileURLToPath(new URL('../../../../adapters/mobile/bridge-runtime', import.meta.url));
35
35
  }
36
36
 
37
37
  export function bridgeEnv(input) {
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { bridgeCommand, runAdapter } from '../platform/bridge.mjs';
3
- import { walletFixturePath } from '../../../../runner/src/paths.ts';
3
+ import { walletFixturePath } from '../../../../src/index.ts';
4
4
 
5
5
  async function fixturePassword(projectRoot) {
6
6
  const candidates = [
@@ -3,7 +3,7 @@ import { spawn } from 'node:child_process';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { bridgeCommand, bridgeEnv, runAdapter } from '../platform/bridge.mjs';
6
- import { walletFixturePath } from '../../../../runner/src/paths.ts';
6
+ import { walletFixturePath } from '../../../../src/index.ts';
7
7
 
8
8
  async function fixtureProfile(projectRoot) {
9
9
  const candidates = [
@@ -109,7 +109,7 @@ function setupWalletScript() {
109
109
  if (process.env.METAMASK_RECIPE_MOBILE_SETUP_WALLET_SCRIPT) {
110
110
  return process.env.METAMASK_RECIPE_MOBILE_SETUP_WALLET_SCRIPT;
111
111
  }
112
- return fileURLToPath(new URL('../bridge-runtime/setup-wallet.sh', import.meta.url));
112
+ return fileURLToPath(new URL('../../../../adapters/mobile/bridge-runtime/setup-wallet.sh', import.meta.url));
113
113
  }
114
114
 
115
115
  function runSetupWallet(input, fixture) {
@@ -2,6 +2,6 @@
2
2
  "kind": "recipe-library",
3
3
  "schema_version": 1,
4
4
  "name": "metamask",
5
- "owner": "metamask-recipe-runner",
5
+ "owner": "mm-harness-runner",
6
6
  "description": "Canonical MetaMask recipe library: minimal, stable flows every recipe can compose. Team and personal libraries layer on top and win resolution by default."
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -8,7 +8,7 @@
8
8
  "scripts": {
9
9
  "dev:link-farmslot": "node scripts/link-local-farmslot.mjs",
10
10
  "check": "node scripts/check.mjs",
11
- "self-test": "orchestration/porcelain/metamask-recipe self-test",
11
+ "self-test": "bin/mm-harness self-test",
12
12
  "manifest:mobile": "bin/mm-harness actions --raw --adapter mobile --json",
13
13
  "manifest:extension": "bin/mm-harness actions --raw --adapter extension --json",
14
14
  "check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
@@ -24,12 +24,12 @@
24
24
  "@types/node": "^22.0.0",
25
25
  "typescript": "^5.6.0"
26
26
  },
27
- "main": "./runner/src/index.ts",
28
- "types": "./runner/src/index.ts",
27
+ "main": "./src/index.ts",
28
+ "types": "./src/index.ts",
29
29
  "exports": {
30
30
  ".": {
31
- "types": "./runner/src/index.ts",
32
- "import": "./runner/src/index.ts"
31
+ "types": "./src/index.ts",
32
+ "import": "./src/index.ts"
33
33
  }
34
34
  },
35
35
  "publishConfig": {
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "files": [
39
39
  "bin",
40
- "runner",
41
- "orchestration",
40
+ "src",
41
+ "adapters",
42
42
  "library",
43
43
  "scripts/completions.sh",
44
44
  "scripts/install-completions.sh",
@@ -3,7 +3,6 @@
3
3
  import { execFileSync } from 'node:child_process';
4
4
  import crypto from 'node:crypto';
5
5
  import fs from 'node:fs';
6
- import os from 'node:os';
7
6
  import path from 'node:path';
8
7
  import {
9
8
  depsCheck,
@@ -11,7 +10,7 @@ import {
11
10
  recordDepsBaseline,
12
11
  type DepsCheck,
13
12
  } from '@farmslot/recipe-harness/runtime/deps-readiness';
14
- import { recipeWatchLogCandidates } from '../../runner/src/paths.ts';
13
+ import { recipeHarnessPath, recipeWatchLogCandidates } from '../../paths.ts';
15
14
 
16
15
  /**
17
16
  * Deterministic runtime-readiness decision for the MetaMask Extension.
@@ -181,10 +180,11 @@ function webpackFingerprint(target: string): { gitHead: string; fingerprint: str
181
180
  return { gitHead: git(target, ['rev-parse', 'HEAD']) ?? 'unknown', fingerprint: hash.digest('hex') };
182
181
  }
183
182
 
184
- // ── per-checkout baseline store (machine-local; never written into the repo) ──
183
+ // ── per-checkout baseline store (checkout-local; not written into the repo) ──
184
+ // State lives under the checkout's own harness dir so it is scoped to the
185
+ // checkout, survives /tmp cleanup, and is not in a world-writable directory.
185
186
  function stateDir(target: string): string {
186
- const key = crypto.createHash('sha1').update(path.resolve(target)).digest('hex').slice(0, 16);
187
- return path.join(os.tmpdir(), 'metamask-recipe-decision', key);
187
+ return recipeHarnessPath(target, 'extension', 'decision-state');
188
188
  }
189
189
 
190
190
  function readBaseline(target: string, name: string): Record<string, unknown> | null {
@@ -4,7 +4,7 @@ import { spawn } from 'node:child_process';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
 
7
- import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, recipeRuntimeDir, resolveLocalProtocolRoot, resolveRequiredLocalProtocolRoot, runnerDir } from '../../runner/src/paths.ts';
7
+ import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, recipeRuntimeDir, resolveLocalProtocolRoot, resolveRequiredLocalProtocolRoot, runnerDir } from '../../paths.ts';
8
8
 
9
9
  // Runtime health uses package dependencies. Launching a host-managed validation
10
10
  // browser remains a dev-only path because it needs pool/project scripts from a