@deeeed/metamask-harness 0.17.0 → 0.17.2
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.
- package/CHANGELOG.md +34 -0
- package/README.md +90 -140
- package/adapters/core/cleanup.sh +0 -0
- package/adapters/core/inject.sh +0 -0
- package/adapters/extension/cleanup.mjs +0 -0
- package/adapters/extension/console-tail.mjs +51 -24
- package/adapters/extension/ensure-browser.sh +0 -0
- package/adapters/extension/inject.mjs +5 -9
- package/adapters/extension/launch-browser.cjs +0 -0
- package/adapters/extension/launch-webpack.cjs +83 -0
- package/adapters/extension/launch.sh +8 -11
- package/adapters/extension/live.sh +2 -0
- package/adapters/extension/readiness.mjs +0 -0
- package/adapters/extension/reattach.sh +1 -0
- package/adapters/extension/refresh-build.sh +0 -0
- package/adapters/extension/seed-fixture.sh +4 -12
- package/adapters/extension/sidepanel-toggle.sh +4 -1
- package/adapters/extension/snapshot-dist.sh +0 -0
- package/adapters/extension/stamp-runtime-title.cjs +49 -0
- package/adapters/extension/start-watch.sh +16 -4
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/sync-webpack-dist.cjs +105 -0
- package/adapters/extension/verify.sh +1 -1
- package/adapters/extension/wallet-fixture-state.cjs +0 -0
- package/adapters/manifest.json +46 -38
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
- package/adapters/mobile/bridge-runtime/setup-wallet.sh +0 -0
- package/adapters/mobile/cleanup.sh +16 -176
- package/adapters/mobile/inject.sh +88 -671
- package/adapters/mobile/launch-console-forwarder.cjs +39 -0
- package/adapters/mobile/lib/metro-listener.sh +0 -0
- package/adapters/mobile/lib/tmux-viewer.sh +0 -0
- package/adapters/mobile/open-device.sh +0 -0
- package/adapters/mobile/prewarm-bundle.sh +0 -0
- package/adapters/mobile/start-console-forwarder.sh +70 -0
- package/adapters/mobile/start-metro.sh +0 -41
- package/adapters/mobile/stop-metro.sh +0 -0
- package/adapters/mobile/verify.sh +4 -61
- package/adapters/mobile/wait-for-bridge.sh +0 -0
- package/adapters/mobile/yarn-setup.sh +0 -0
- package/adapters/shared/activate-repo-node.sh +0 -0
- package/adapters/shared/activate-repo-ruby.sh +0 -0
- package/adapters/shared/cli-ux.sh +0 -0
- package/adapters/shared/ensure-runner-deps.sh +0 -0
- package/adapters/shared/harness-path.sh +0 -0
- package/adapters/shared/hash-helpers.sh +0 -0
- package/adapters/shared/install-repo-deps.sh +0 -0
- package/adapters/shared/json-field.sh +0 -0
- package/adapters/shared/open-debug.mjs +90 -36
- package/adapters/shared/open-log-window.sh +0 -0
- package/adapters/shared/reap-checkout-metros.sh +0 -0
- package/adapters/shared/resolve-slot-ports-core.mjs +0 -6
- package/adapters/shared/resolve-slot-ports.mjs +0 -0
- package/adapters/shared/resolve-slot-ports.sh +0 -5
- package/adapters/shared/sync-wallet-fixture.sh +0 -0
- package/adapters/shared/tmux-session.sh +0 -0
- package/adapters/shared/tmux-viewer.sh +0 -0
- package/adapters/shared/update-check-worker.mjs +37 -0
- package/dist/adapters/extension/console-capture.js +84 -0
- package/dist/adapters/extension/runtime-decision.js +14 -2
- package/dist/adapters/extension/surface.js +3 -2
- package/dist/adapters/mobile/prepare.js +22 -7
- package/dist/adapters/mobile/surface.js +1 -1
- package/dist/adapters/resolve-slot-ports.js +0 -2
- package/dist/adapters/slot-ports.js +8 -0
- package/dist/cli.js +3 -11
- package/dist/commands/call.js +9 -4
- package/dist/commands/debug.js +38 -31
- package/dist/commands/fixtures.js +18 -10
- package/dist/commands/launch/index.js +10 -0
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +5 -17
- package/dist/commands/run-engine.js +1 -32
- package/dist/commands/run-report.js +1 -1
- package/dist/commands/run.js +50 -3
- package/dist/commands/status-probe.js +1 -1
- package/dist/commands/update.js +15 -28
- package/dist/doctor.js +1 -4
- package/dist/harness.js +5 -123
- package/dist/live-adapter-contract.js +6 -20
- package/dist/mm-harness-cli.js +6 -43
- package/dist/paths.js +2 -5
- package/dist/run-diagnostics.js +35 -40
- package/dist/runtime-context.js +26 -8
- package/docs/CHEATSHEET.md +39 -52
- package/docs/CLI-ERGONOMICS-HUMAN-QA.md +48 -104
- package/docs/CODE-MAP.md +1 -1
- package/docs/VIDEO-DEMO-VALIDATION.md +62 -0
- package/docs/architecture.md +65 -375
- package/docs/live-adapter-contract.md +3 -1
- package/library/actions/core/perps/_controller.mjs +10 -55
- package/library/manifests/mobile.action-manifest.json +1 -1
- package/package.json +2 -2
- package/scripts/completions.sh +0 -0
- package/scripts/install-completions.sh +0 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
- package/adapters/mobile/overlay/compat/README.md +0 -28
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
- package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
- package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
- package/adapters/shared/resolve-farmslot-ports.sh +0 -7
- package/docs/CLI-SPEC.md +0 -1142
- package/docs/MENTAL-MODEL.md +0 -295
package/docs/architecture.md
CHANGED
|
@@ -1,398 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Architecture
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
38
|
-
This repo is the MetaMask-specific adapter layer for Recipe Protocol v1. It is
|
|
39
|
-
not the protocol engine, not a skills repo, not a slot farm, and not product
|
|
40
|
-
code. Its job is to publish reviewable MetaMask capabilities and run them
|
|
41
|
-
against a local Mobile or Extension checkout without committing harness runtime
|
|
42
|
-
into that checkout.
|
|
43
|
-
|
|
44
|
-
The core model is standalone: install or check out this runner, point it at a
|
|
45
|
-
MetaMask checkout, and run `mm-harness`. Farm/slot orchestration is only a
|
|
46
|
-
way to scale the same loop across many checkouts, machines, ports, and agents;
|
|
47
|
-
it is not required to understand or use the runner.
|
|
48
|
-
|
|
49
|
-
## TL;DR
|
|
50
|
-
|
|
51
|
-
The four questions that come up whenever this repo needs explaining.
|
|
52
|
-
|
|
53
|
-
**Responsibility split.** This runner owns the logic; skills and farms are a
|
|
54
|
-
minimum overlay that call its CLI. Rule of thumb: if a skill's shell script is
|
|
55
|
-
doing more than parsing flags and calling `mm-harness`, that logic
|
|
56
|
-
belongs in this runner instead. Concrete example in flight: the
|
|
57
|
-
`recipe-harness` skill currently locates and execs runner internals in
|
|
58
|
-
hundreds of lines of bash (resolve runner source, dispatch to
|
|
59
|
-
`adapters/<platform>/{inject,cleanup}.{sh,mjs}` — `.sh` for Mobile/Core,
|
|
60
|
-
`.mjs` for Extension); the fix is a
|
|
61
|
-
[`mm-harness <install|verify|cleanup|live>`](CLI-SPEC.md) command here, so the
|
|
62
|
-
skill thins to a few lines that call it. Same pattern applies to every other
|
|
63
|
-
`recipe-*` skill over time.
|
|
64
|
-
|
|
65
|
-
**Language policy — bash vs ts vs mjs vs cjs.** Current mix: 65 `.sh`, 62
|
|
66
|
-
`.mjs`, 17 `.ts`, 9 `.cjs` (excluding `node_modules`).
|
|
67
|
-
|
|
68
|
-
| Extension | Where | Use for | Why |
|
|
69
|
-
|---|---|---|---|
|
|
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 |
|
|
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 |
|
|
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 |
|
|
74
|
-
|
|
75
|
-
The mix is bash-heavy today because sandbox setup crosses OS/device boundaries
|
|
76
|
-
(see "Why there are shell scripts" below) — that is current reality, not the
|
|
77
|
-
target ratio. The direction is less bash over time: when a shell helper starts
|
|
78
|
-
accumulating domain behavior, move it into `.ts` or a focused `.mjs` adapter
|
|
79
|
-
(see [Runtime File Conventions](runtime-file-conventions.md) for the full
|
|
80
|
-
rule).
|
|
81
|
-
|
|
82
|
-
**Packaging — one package, not several.** This stays one npm package while the
|
|
83
|
-
runtime API is still moving; see [Package Boundaries](package-boundaries.md)
|
|
84
|
-
for the full rationale. Splitting now would force every wrapper (skills,
|
|
85
|
-
farmslot) to pin two coordinated versions before the boundary between "recipe
|
|
86
|
-
execution" and "runtime lifecycle" has stabilized — coordination cost for no
|
|
87
|
-
current benefit. Split when the runtime CLI contract is stable, all wrappers
|
|
88
|
-
call it exclusively (no copied adapter scripts), and the two areas have gone
|
|
89
|
-
several releases without a change that touches both.
|
|
90
|
-
|
|
91
|
-
## Layer map (front door → engine → schema)
|
|
92
|
-
|
|
93
|
-
The whole stack is three layers with a strictly one-way dependency. `mm-harness`
|
|
94
|
-
is the MetaMask-facing **front door**; underneath it the runner is a thin
|
|
95
|
-
**adapter layer** over a fat generic **engine** (`@farmslot/recipe-harness`) and
|
|
96
|
-
**schema** (`@farmslot/protocol`). The runner never re-implements engine logic —
|
|
97
|
-
it imports it.
|
|
98
|
-
|
|
99
|
-
```text
|
|
100
|
-
mm-harness (the one bin — no per-platform binaries) ← front door: one verb vocabulary
|
|
101
|
-
│ launch · logs · debug · fixtures (daily loop) · run/flows/doctor/actions/manifest (prove)
|
|
102
|
-
│ install/verify/cleanup/live (runtime overlay lifecycle)
|
|
103
|
-
▼
|
|
104
|
-
metamask-recipe-runner (MetaMask ADAPTER layer) ← this repo
|
|
105
|
-
│ MetaMask action manifests, metamask.* live adapters, Mobile/Extension
|
|
106
|
-
│ runtime overlay install, runtime health/decision probes
|
|
107
|
-
▼
|
|
108
|
-
@farmslot/recipe-harness (GENERIC engine) ← npm dependency
|
|
109
|
-
│ recipe graph execution, official ui.* transports, RecordingTarget
|
|
110
|
-
│ evidence framework, runtime modules, generic run/flows/validate CLI
|
|
111
|
-
▼
|
|
112
|
-
@farmslot/protocol (GENERIC schema) ← npm dependency
|
|
113
|
-
│ Recipe v1 schema, action-manifest document, artifact contract
|
|
114
|
-
▼
|
|
115
|
-
MetaMask app under test
|
|
116
|
-
│ local debug runtime only; product source should not own runner logic
|
|
117
|
-
▼
|
|
118
|
-
summary.json · trace.json · artifact-manifest.json · screenshots/logs
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Dependency direction is one-way and provable.** The runner *depends on*
|
|
122
|
-
`@farmslot/recipe-harness` + `@farmslot/protocol` (see `package.json`), and
|
|
123
|
-
`src/**` *imports* the generic contracts rather than forking them:
|
|
124
|
-
|
|
125
|
-
- `src/cli.ts` imports `RecipeRunResult` from `@farmslot/recipe-harness`
|
|
126
|
-
and delegates library `flows` to the harness CLI (`importRecipeHarnessCli`).
|
|
127
|
-
- `src/runner.ts` wires `metamask.*` adapters INTO the farmslot
|
|
128
|
-
`RecipeRunner`; it does not implement graph execution.
|
|
129
|
-
- `src/manifest.ts` loads *MetaMask* manifests typed by the *farmslot*
|
|
130
|
-
protocol document type.
|
|
131
|
-
- `src/recording-target.ts` is a MetaMask provider over the farmslot
|
|
132
|
-
`RecordingTarget` contract.
|
|
133
|
-
|
|
134
|
-
**The invariant both repos assert:** farmslot stays MetaMask-free; the runner
|
|
135
|
-
never re-implements engine logic, only binds to it. Decision lens for placing any
|
|
136
|
-
new capability: *would a non-MetaMask project reuse this unchanged?* → yes =
|
|
137
|
-
generic (farmslot); encodes MetaMask app knowledge = runner.
|
|
138
|
-
|
|
139
|
-
> Note on naming: "harness" refers to the tool (`mm-harness`) and the generic
|
|
140
|
-
> engine package (`@farmslot/recipe-harness`). The per-checkout files the tool
|
|
141
|
-
> installs into a target are the **runtime overlay** (`install`/`verify`/
|
|
142
|
-
> `cleanup`/`live`), never "the harness" — see [CLI-SPEC.md](CLI-SPEC.md).
|
|
143
|
-
|
|
144
|
-
## One-minute model
|
|
3
|
+
`mm-harness` is the MetaMask adapter and CLI over the generic Recipe Protocol
|
|
4
|
+
engine. Control flows downward only:
|
|
145
5
|
|
|
146
6
|
```text
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
│ generic graph execution, official ui.* transports, traces, artifacts
|
|
155
|
-
▼
|
|
156
|
-
Recipe protocol package
|
|
157
|
-
│ Recipe v1 schema, manifest contract, artifact contract
|
|
158
|
-
▼
|
|
159
|
-
MetaMask app under test
|
|
160
|
-
│ local debug runtime only; product source should not own runner logic
|
|
161
|
-
▼
|
|
162
|
-
summary.json · trace.json · artifact-manifest.json · screenshots/logs
|
|
7
|
+
bin/mm-harness
|
|
8
|
+
→ src/ typed CLI, decisions, orchestration, evidence
|
|
9
|
+
→ @farmslot/recipe-harness generic graph execution and transports
|
|
10
|
+
→ @farmslot/protocol recipe, manifest, trace, artifact schemas
|
|
11
|
+
→ adapters/ focused OS, browser, simulator, and device leaves
|
|
12
|
+
→ library/ MetaMask actions, flows, recipes, manifests
|
|
13
|
+
→ MetaMask checkout app under test
|
|
163
14
|
```
|
|
164
15
|
|
|
165
|
-
|
|
166
|
-
|
|
16
|
+
Nothing under `adapters/` or `library/` calls back into `mm-harness`. TypeScript
|
|
17
|
+
owns sequencing; a leaf performs one platform operation and exits; the library
|
|
18
|
+
defines composable behavior.
|
|
167
19
|
|
|
168
|
-
##
|
|
20
|
+
## Ownership
|
|
169
21
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
## The two subsystems
|
|
173
|
-
|
|
174
|
-
This repo has two intentionally different kinds of code. Keeping them separate
|
|
175
|
-
is the main way to understand the repository.
|
|
176
|
-
|
|
177
|
-
| Subsystem | Question it answers | Primary files | Should contain | Should not contain |
|
|
178
|
-
|---|---|---|---|---|
|
|
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 |
|
|
181
|
-
|
|
182
|
-
When reviewing a change, first decide which subsystem it touches. Recipe changes
|
|
183
|
-
should be validated against manifests and action artifacts. Runtime lifecycle changes should be validated by install/launch/live/verify
|
|
184
|
-
behavior on a real checkout. Some commands cross the boundary, but they should
|
|
185
|
-
do so by delegating: sandbox helpers get the runtime ready, then
|
|
186
|
-
`mm-harness run` executes the recipe.
|
|
187
|
-
|
|
188
|
-
## Ownership boundaries
|
|
189
|
-
|
|
190
|
-
| Layer | Owns | Must not own |
|
|
22
|
+
| Layer | Owns | Does not own |
|
|
191
23
|
|---|---|---|
|
|
192
|
-
|
|
|
193
|
-
|
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
|
|
|
24
|
+
| `@farmslot/protocol` | Recipe and evidence schemas | execution or product behavior |
|
|
25
|
+
| `@farmslot/recipe-harness` | graph execution, generic `ui.*`, traces and artifacts | MetaMask behavior |
|
|
26
|
+
| this package | MetaMask actions, runtime control, CLI, diagnostics | task acceptance criteria or copied engine logic |
|
|
27
|
+
| skills/checklists | task workflow and proof expectations | runtime or runner implementation |
|
|
28
|
+
| product checkout | application and explicit debug hooks | harness scripts or private workflow logic |
|
|
197
29
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
30
|
+
A capability reusable without MetaMask belongs in the generic engine. A durable
|
|
31
|
+
MetaMask operation belongs here. Ticket-specific assertions stay in the task's
|
|
32
|
+
recipe or checklist.
|
|
201
33
|
|
|
202
|
-
##
|
|
34
|
+
## Repository layout
|
|
203
35
|
|
|
204
36
|
| Path | Responsibility |
|
|
205
37
|
|---|---|
|
|
206
|
-
| `
|
|
207
|
-
| `src/
|
|
208
|
-
| `src/adapters
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `library/actions
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Defaults are centralized in `adapters/shared/path-defaults.json`:
|
|
226
|
-
|
|
227
|
-
```json
|
|
228
|
-
{
|
|
229
|
-
"recipeHarnessRoot": "temp/recipe/harness",
|
|
230
|
-
"recipeRuntimeDir": "temp/recipe/runtime"
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
All shell, standalone Node, and TypeScript code must read these through the
|
|
235
|
-
shared helpers instead of hardcoding defaults. Environment overrides are allowed
|
|
236
|
-
through `RECIPE_HARNESS_ROOT` and `RECIPE_RUNTIME_DIR`, but they must stay safe
|
|
237
|
-
relative paths.
|
|
238
|
-
|
|
239
|
-
Install commands write a small runtime package into the target checkout:
|
|
38
|
+
| `bin/mm-harness` | one public executable |
|
|
39
|
+
| `src/commands/` | public command behavior |
|
|
40
|
+
| `src/adapters/` | typed product decisions and orchestration |
|
|
41
|
+
| `adapters/<platform>/` | focused shell/Node runtime leaves |
|
|
42
|
+
| `adapters/shared/` | cross-platform OS glue |
|
|
43
|
+
| `adapters/*/overlay/` | the only files installed or patched into a checkout |
|
|
44
|
+
| `library/actions/` | executable MetaMask action adapters |
|
|
45
|
+
| `library/flows/` | reusable parameterized behavior |
|
|
46
|
+
| `library/recipes/` | reusable proof recipes |
|
|
47
|
+
| `library/manifests/` | declared capability surface |
|
|
48
|
+
| `scripts/` | repository development checks only |
|
|
49
|
+
|
|
50
|
+
All TypeScript belongs under `src/`. New standalone runtime helpers use ESM
|
|
51
|
+
unless their host runtime requires CommonJS. Shell remains limited to operations
|
|
52
|
+
that genuinely need shell/device tools.
|
|
53
|
+
|
|
54
|
+
## Runtime isolation
|
|
55
|
+
|
|
56
|
+
Each checkout owns its runtime state:
|
|
240
57
|
|
|
241
58
|
```text
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
action-manifest.json snapshot of the adapter manifest
|
|
245
|
-
runner/bin/mm-harness delegate back to the resolved runner source
|
|
246
|
-
runner/recipes/ reusable recipe snapshot
|
|
247
|
-
scripts/ adapter runtime helpers copied from this repo
|
|
59
|
+
temp/recipe/runtime/ ports, processes, fixture, logs, runtime identity
|
|
60
|
+
temp/recipe/harness/ installed runtime overlay
|
|
248
61
|
```
|
|
249
62
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
## Recipe execution vs sandbox lifecycle
|
|
255
|
-
|
|
256
|
-
`mm-harness run <recipe.json> --adapter ...` is the recipe path. It creates
|
|
257
|
-
a shared Recipe runner (`src/runner.ts`), validates the recipe against the
|
|
258
|
-
manifest, executes nodes, and writes artifacts. If a bug is about action fields,
|
|
259
|
-
trace output, adapter semantics, or whether a recipe proves a claim, start in
|
|
260
|
-
`library/manifests/`, `src/`, `library/actions/`, and `library/recipes/`.
|
|
261
|
-
|
|
262
|
-
`prepare`, `launch`, `live`, `verify`, `status`, `decision`, and `ready` are
|
|
263
|
-
sandbox lifecycle paths. They give the agent a reproducible local app session:
|
|
264
|
-
Mobile with Metro/dev-client/simulator and the bridge online; Extension with an
|
|
265
|
-
isolated browser profile, unpacked extension loaded, and a known home/popup-style
|
|
266
|
-
UI target. If a bug is about Metro, bundle prewarm, simulator launch, Chrome CDP,
|
|
267
|
-
Extension full-screen vs popup presentation, build freshness, wallet fixture
|
|
268
|
-
placement, git-exclude, or cleanup, start in `adapters/{mobile,extension,core}/`.
|
|
269
|
-
|
|
270
|
-
Do not put recipe graph traversal into shell scripts. Shell scripts may prepare
|
|
271
|
-
or inspect the sandboxed runtime, then delegate graph execution to
|
|
272
|
-
`mm-harness run`.
|
|
273
|
-
|
|
274
|
-
## HUD vs bridge vs product hooks
|
|
275
|
-
|
|
276
|
-
These names are easy to mix up; they are different concerns.
|
|
277
|
-
|
|
278
|
-
| Term | What it is | Why it exists |
|
|
279
|
-
|---|---|---|
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
284
|
-
|
|
285
|
-
The HUD does not control the app. The bridge/CDP control the app. The HUD only
|
|
286
|
-
renders proof context.
|
|
287
|
-
|
|
288
|
-
## Mobile runtime shape
|
|
289
|
-
|
|
290
|
-
Mobile has the most moving parts because React Native does not expose a browser
|
|
291
|
-
DOM by default.
|
|
292
|
-
|
|
293
|
-
```text
|
|
294
|
-
mm-harness launch ios|android
|
|
295
|
-
│ prepareMobile() in-process → yarn-setup / start-metro / open-device leaves
|
|
296
|
-
▼
|
|
297
|
-
adapters/mobile/bridge-runtime/cdp-bridge.cjs
|
|
298
|
-
│ connects to Hermes / RN debug runtime
|
|
299
|
-
▼
|
|
300
|
-
globalThis.__AGENTIC__ inside the app
|
|
301
|
-
│ route/status/wallet/ui commands + optional HUD rendering
|
|
302
|
-
▼
|
|
303
|
-
library/actions/mobile/{wallet,perps,ui,platform}/*.mjs
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
Ideally the app would expose a product-owned debug automation surface directly,
|
|
307
|
-
so the runner would not need to patch or inject Mobile client files at all. The
|
|
308
|
-
current injection exists as a compatibility bridge: it lets recipes run against
|
|
309
|
-
older Mobile versions, historical PR branches, and eval/replay runs that do not yet include that
|
|
310
|
-
client-side automation surface.
|
|
311
|
-
|
|
312
|
-
The Mobile injection currently may touch development-only product files on older
|
|
313
|
-
checkouts to install the `AgenticService`, navigation hook, and HUD mount. That
|
|
314
|
-
is the fragile compatibility path, not the desired long-term product contract.
|
|
315
|
-
Those product patches are local runtime state: never commit them to MetaMask
|
|
316
|
-
Mobile. The runner also installs ignored helper files under
|
|
317
|
-
`temp/recipe/harness/mobile`.
|
|
318
|
-
|
|
319
|
-
Mobile `ensure_*` actions must be idempotent: if the wallet is already unlocked,
|
|
320
|
-
`metamask.wallet.ensure_unlocked` should report success or converge cheaply, not
|
|
321
|
-
fail because the starting state differed.
|
|
322
|
-
|
|
323
|
-
## Extension runtime shape
|
|
324
|
-
|
|
325
|
-
Extension does not need an in-product source patch. The runner works through an
|
|
326
|
-
unpacked `dist/chrome` build and Chrome CDP.
|
|
327
|
-
|
|
328
|
-
```text
|
|
329
|
-
mme-recipe / adapters/extension/*.sh
|
|
330
|
-
│ checks dist freshness, build health, fixture/profile state
|
|
331
|
-
▼
|
|
332
|
-
Chrome for Testing with --load-extension=<runtime-dist>
|
|
333
|
-
│ CDP target discovery + deterministic extension id
|
|
334
|
-
▼
|
|
335
|
-
library/actions/extension/{wallet,perps,ui,platform}/*.mjs
|
|
336
|
-
│ extension page/background hooks and UI events
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
`runtime-launch --start-watch` is the clean-build path. Without `--start-watch`,
|
|
340
|
-
verify can prove the existing runtime is reachable but may fail `dist-freshness`
|
|
341
|
-
if `dist/chrome` does not match `HEAD`. That failure is intentional: it prevents
|
|
342
|
-
silently proving stale product code.
|
|
343
|
-
|
|
344
|
-
## Why there are shell scripts
|
|
345
|
-
|
|
346
|
-
The shell scripts are large because sandbox setup crosses OS/device boundaries:
|
|
347
|
-
`simctl`, `adb`, Metro, Watchman, Chrome process flags, isolated browser
|
|
348
|
-
profiles, git exclude files, symlink safety checks, and cleanup all live outside
|
|
349
|
-
Node's typed domain logic. Their purpose is to give an agent a reliable app
|
|
350
|
-
session, not to define recipe semantics.
|
|
351
|
-
|
|
352
|
-
Allowed in shell:
|
|
353
|
-
|
|
354
|
-
- parsing CLI flags for lifecycle commands;
|
|
355
|
-
- calling OS/device tools;
|
|
356
|
-
- copying/removing installed harness files;
|
|
357
|
-
- starting/stopping/reusing local dev servers;
|
|
358
|
-
- writing small runtime summaries from command results.
|
|
359
|
-
|
|
360
|
-
Not allowed in shell:
|
|
361
|
-
|
|
362
|
-
- Recipe v1 graph execution;
|
|
363
|
-
- MetaMask domain semantics that can live in `src/**/*.ts` or `library/actions/**/*.mjs`;
|
|
364
|
-
- duplicated action manifest logic;
|
|
365
|
-
- product-specific business decisions beyond runtime boot/health checks.
|
|
366
|
-
|
|
367
|
-
When a shell helper starts accumulating domain behavior, move that behavior into
|
|
368
|
-
TypeScript or a focused `.mjs` adapter and keep shell as the launcher.
|
|
369
|
-
|
|
370
|
-
## Adding or changing capabilities
|
|
63
|
+
Defaults live in `adapters/shared/path-defaults.json`. Every browser profile,
|
|
64
|
+
simulator/device selection, port, watcher, and evidence directory must resolve
|
|
65
|
+
from the current checkout so parallel slots do not share state.
|
|
371
66
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
4. Ensure every `ensure_*` action proves a postcondition.
|
|
377
|
-
5. Use official `ui.*` actions and screenshot claims for visible acceptance
|
|
378
|
-
criteria; do not add task-specific `metamask.*` actions for exact copy,
|
|
379
|
-
styling, ticket IDs, or one-off selectors.
|
|
380
|
-
6. Update smoke/runner.action-validation recipes only when the capability is reusable.
|
|
67
|
+
Extension uses an isolated Chrome profile and CDP port. Its normal watcher builds
|
|
68
|
+
to product `dist/chrome`, then synchronizes successful output into the isolated
|
|
69
|
+
runtime extension. Mobile owns an isolated Metro/device identity. Core remains
|
|
70
|
+
headless.
|
|
381
71
|
|
|
382
|
-
##
|
|
72
|
+
## Proof boundary
|
|
383
73
|
|
|
384
|
-
|
|
74
|
+
`launch` prepares the runtime. `run` validates and executes a recipe through the
|
|
75
|
+
generic engine. Runtime setup may seed declared fixtures before proof begins;
|
|
76
|
+
proof actions must not mutate hidden product state to fabricate a result.
|
|
385
77
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
4. run `mm-harness run <recipe.json> --adapter <platform> --artifacts-dir <dir>`.
|
|
78
|
+
Each run produces a summary, trace, diagnostics where applicable, and an artifact
|
|
79
|
+
manifest. Application warnings/errors are side findings unless a recipe assertion
|
|
80
|
+
turns them into a proof failure.
|
|
390
81
|
|
|
391
|
-
|
|
392
|
-
the runner source and package evidence, but it should dispatch `install`,
|
|
393
|
-
`launch`, `live`, `verify`, and `cleanup` to this repo or to the installed
|
|
394
|
-
harness helper. It should not carry Mobile or Extension adapter scripts.
|
|
82
|
+
## Change checks
|
|
395
83
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
84
|
+
- Put the change in the owning layer; do not duplicate behavior in a skill or leaf.
|
|
85
|
+
- Keep actions parameterized and manifests honest.
|
|
86
|
+
- Validate the machine contract and the visible human outcome.
|
|
87
|
+
- Exercise affected platforms on real checkouts when runtime behavior changes.
|
|
88
|
+
- Run `yarn check` and update `CHANGELOG.md` for user-visible changes.
|
|
@@ -19,7 +19,9 @@ $METAMASK_RECIPE_LIVE_ADAPTER_DIR/shared/<domain>/<action-local-name>.mjs
|
|
|
19
19
|
<runner>/library/actions/shared/<domain>/<action-local-name>.mjs
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Action adapters are grouped `.mjs` modules. Flat files, CommonJS, and shell
|
|
23
|
+
adapters are not part of the contract. Do not add `ui.*` files here; official UI
|
|
24
|
+
actions go through shared base transports. Examples:
|
|
23
25
|
|
|
24
26
|
```text
|
|
25
27
|
library/actions/extension/perps/ensure_positions.mjs
|
|
@@ -90,12 +90,7 @@ function buildInfrastructure(stubbed) {
|
|
|
90
90
|
// The fixture has the standard { accounts: [{ type, value, name }] } shape from
|
|
91
91
|
// wallet-fixture.json.sample. The recipe node selects an account by name via
|
|
92
92
|
// `account_name` (default "dev1"). The viem account derived from the fixture
|
|
93
|
-
// entry is the authoritative source for both the address
|
|
94
|
-
// no separate MM_TEST_ACCOUNT_ADDRESS env var needed.
|
|
95
|
-
//
|
|
96
|
-
// Env-var fallback: if no fixture is present (e.g. direct CLI invocation),
|
|
97
|
-
// MM_TEST_ACCOUNT_SRP / MM_TEST_ACCOUNT_PRIVATE_KEY + MM_TEST_ACCOUNT_ADDRESS
|
|
98
|
-
// are still accepted for backward compatibility.
|
|
93
|
+
// entry is the authoritative source for both the address and signing key.
|
|
99
94
|
|
|
100
95
|
/**
|
|
101
96
|
* Load wallet-fixture.json and return the named account entry.
|
|
@@ -155,26 +150,6 @@ function viemAccountFromFixtureEntry(entry) {
|
|
|
155
150
|
return privateKeyToAccount(normalized);
|
|
156
151
|
}
|
|
157
152
|
|
|
158
|
-
// Env-var fallback constants (used only when wallet-fixture.json is absent).
|
|
159
|
-
const SIGNER_PRIVATE_KEY_ENV = 'MM_TEST_ACCOUNT_PRIVATE_KEY';
|
|
160
|
-
const SIGNER_MNEMONIC_ENV = 'MM_TEST_ACCOUNT_SRP';
|
|
161
|
-
|
|
162
|
-
function signerFromEnv() {
|
|
163
|
-
const pk = process.env[SIGNER_PRIVATE_KEY_ENV]?.trim();
|
|
164
|
-
if (pk && pk.length > 0) {
|
|
165
|
-
const normalized = pk.startsWith('0x') ? pk : `0x${pk}`;
|
|
166
|
-
if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
|
|
167
|
-
throw new Error(`${SIGNER_PRIVATE_KEY_ENV} is not a 32-byte hex private key.`);
|
|
168
|
-
}
|
|
169
|
-
return privateKeyToAccount(normalized);
|
|
170
|
-
}
|
|
171
|
-
const mnemonic = process.env[SIGNER_MNEMONIC_ENV]?.trim();
|
|
172
|
-
if (mnemonic && mnemonic.split(/\s+/u).length >= 12) {
|
|
173
|
-
return mnemonicToAccount(mnemonic, { addressIndex: 0 });
|
|
174
|
-
}
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
153
|
/**
|
|
179
154
|
* Resolve the account name to use for signing.
|
|
180
155
|
* Precedence: node.account_name → node.account (if not an address) → "dev1".
|
|
@@ -198,9 +173,7 @@ function resolveAccountName(input) {
|
|
|
198
173
|
|
|
199
174
|
/**
|
|
200
175
|
* Resolve the viem signer and EVM address for writes.
|
|
201
|
-
*
|
|
202
|
-
* Fallback: MM_TEST_ACCOUNT_PRIVATE_KEY / MM_TEST_ACCOUNT_SRP env vars
|
|
203
|
-
* (requires MM_TEST_ACCOUNT_ADDRESS for address verification).
|
|
176
|
+
* The wallet fixture account selected by name is the only signing source.
|
|
204
177
|
*
|
|
205
178
|
* @param input - Adapter input (context.projectRoot, node.account_name).
|
|
206
179
|
* @returns { account: ViemAccount, address: string }
|
|
@@ -209,7 +182,6 @@ async function resolveSignerFromFixture(input) {
|
|
|
209
182
|
const projectRoot = input.context?.projectRoot;
|
|
210
183
|
const accountName = resolveAccountName(input);
|
|
211
184
|
|
|
212
|
-
// Primary: fixture
|
|
213
185
|
if (projectRoot) {
|
|
214
186
|
const entry = await loadFixtureAccount(projectRoot, accountName);
|
|
215
187
|
if (entry) {
|
|
@@ -218,32 +190,15 @@ async function resolveSignerFromFixture(input) {
|
|
|
218
190
|
}
|
|
219
191
|
}
|
|
220
192
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`core perps writes require a wallet-fixture.json with account "${accountName}", ` +
|
|
226
|
-
`or env vars ${SIGNER_PRIVATE_KEY_ENV} / ${SIGNER_MNEMONIC_ENV} + MM_TEST_ACCOUNT_ADDRESS.`,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const envAddress = String(process.env.MM_TEST_ACCOUNT_ADDRESS ?? '').trim();
|
|
230
|
-
if (!/^0x[0-9a-fA-F]{40}$/u.test(envAddress)) {
|
|
231
|
-
throw new Error(
|
|
232
|
-
`Env-var fallback requires MM_TEST_ACCOUNT_ADDRESS (a 0x EVM address) to verify the signer.`,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
if (account.address.toLowerCase() !== envAddress.toLowerCase()) {
|
|
236
|
-
throw new Error(
|
|
237
|
-
`Env-var signer derives ${account.address} but MM_TEST_ACCOUNT_ADDRESS is ${envAddress}; signatures would be invalid.`,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
return { account, address: envAddress };
|
|
193
|
+
throw new Error(
|
|
194
|
+
`core perps writes require wallet-fixture.json account "${accountName}". ` +
|
|
195
|
+
`Next: run mm-harness fixtures set`,
|
|
196
|
+
);
|
|
241
197
|
}
|
|
242
198
|
|
|
243
199
|
/**
|
|
244
200
|
* Resolve the account address for reads (no signing required).
|
|
245
|
-
*
|
|
246
|
-
* Fallback: node.account / node.address / MM_TEST_ACCOUNT_ADDRESS env var.
|
|
201
|
+
* Prefer the wallet fixture account; explicit node addresses support read-only calls.
|
|
247
202
|
*/
|
|
248
203
|
async function requireAccountAddress(input) {
|
|
249
204
|
const projectRoot = input.context?.projectRoot;
|
|
@@ -261,13 +216,13 @@ async function requireAccountAddress(input) {
|
|
|
261
216
|
}
|
|
262
217
|
}
|
|
263
218
|
|
|
264
|
-
//
|
|
219
|
+
// Read-only calls may name an address directly.
|
|
265
220
|
const fromNode = input.node?.account ?? input.node?.address ?? input.node?.userAddress;
|
|
266
|
-
const address = String(fromNode ??
|
|
221
|
+
const address = String(fromNode ?? '').trim();
|
|
267
222
|
if (!/^0x[0-9a-fA-F]{40}$/u.test(address)) {
|
|
268
223
|
throw new Error(
|
|
269
224
|
`core perps reads require a wallet-fixture.json with account "${accountName}", ` +
|
|
270
|
-
`or a 0x EVM address via node.account
|
|
225
|
+
`or a 0x EVM address via node.account.`,
|
|
271
226
|
);
|
|
272
227
|
}
|
|
273
228
|
return address;
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
]
|
|
157
157
|
},
|
|
158
158
|
"ui.press": {
|
|
159
|
-
"description": "Press a React Native component by exact testID/test_id,
|
|
159
|
+
"description": "Press a React Native component by exact testID/test_id, selector identifier, or contained visible text.",
|
|
160
160
|
"schema": {
|
|
161
161
|
"type": "object",
|
|
162
162
|
"properties": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -71,4 +71,4 @@
|
|
|
71
71
|
"url": "https://github.com/MetaMask/experimental-metamask-harness/issues"
|
|
72
72
|
},
|
|
73
73
|
"homepage": "https://github.com/MetaMask/experimental-metamask-harness#readme"
|
|
74
|
-
}
|
|
74
|
+
}
|
package/scripts/completions.sh
CHANGED
|
File without changes
|
|
File without changes
|