@deeeed/metamask-harness 0.14.0 → 0.14.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 +31 -0
- package/adapters/extension/inject.mjs +1 -0
- package/adapters/extension/launch-browser.cjs +15 -3
- package/adapters/extension/lib/extension-id.cjs +36 -0
- package/adapters/extension/live.sh +5 -3
- package/adapters/extension/readiness.mjs +48 -0
- package/adapters/extension/reattach.sh +163 -55
- package/adapters/extension/sidepanel-toggle.sh +96 -24
- package/adapters/extension/verify.sh +11 -0
- package/adapters/extension/wallet-fixture-state.cjs +6 -11
- package/adapters/mobile/open-device.sh +32 -5
- package/adapters/shared/resolve-slot-ports-core.mjs +14 -4
- package/dist/adapters/core/surface.js +1 -0
- package/dist/adapters/extension/runtime.js +14 -5
- package/dist/adapters/extension/surface.js +1 -0
- package/dist/adapters/mobile/provision.js +49 -4
- package/dist/adapters/mobile/surface.js +1 -0
- package/dist/adapters/slot-ports.js +11 -4
- package/dist/cli.js +4 -0
- package/dist/commands/call.js +19 -2
- package/dist/commands/check.js +326 -0
- package/dist/commands/core-readiness.js +75 -0
- package/dist/commands/device-target.js +113 -10
- package/dist/commands/doctor.js +30 -18
- package/dist/commands/launch/extension.js +105 -9
- package/dist/commands/launch/index.js +116 -15
- package/dist/commands/mobile-device-view.js +140 -0
- package/dist/commands/parse-args.js +4 -1
- package/dist/commands/recipe-quality.js +6 -2
- package/dist/commands/run-engine.js +28 -2
- package/dist/commands/run-report.js +115 -0
- package/dist/commands/run.js +113 -8
- package/dist/commands/shared.js +2 -1
- package/dist/commands/status-probe.js +9 -3
- package/dist/commands/status.js +40 -60
- package/dist/live-adapter-contract.js +5 -1
- package/dist/mm-harness-cli.js +61 -22
- package/docs/CLI-SPEC.md +25 -0
- package/library/actions/extension/platform/cdp.mjs +7 -4
- package/package.json +4 -4
package/dist/mm-harness-cli.js
CHANGED
|
@@ -6,6 +6,8 @@ import { Command } from "commander";
|
|
|
6
6
|
import { color } from "./cli-color.js";
|
|
7
7
|
import { handleUpdate, maybeNudge } from "./commands/update.js";
|
|
8
8
|
import { handleCallHelp } from "./commands/call.js";
|
|
9
|
+
import { getAdapterSurface } from "./adapters/surface.js";
|
|
10
|
+
import { detectAdapter } from "./harness.js";
|
|
9
11
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
12
|
globalThis.__MM_HARNESS_WRAPPER__ = true;
|
|
11
13
|
const { main: recipeMain } = await import("./cli.js");
|
|
@@ -24,9 +26,13 @@ const REAL = [
|
|
|
24
26
|
--target <path> Checkout path (default: cwd)
|
|
25
27
|
--json Machine-readable envelope { adapter, target, devices[], next }
|
|
26
28
|
--fast Skip all live-state probes (instant output, safe for scripts)
|
|
29
|
+
--all-devices Mobile only: show every connected device instead of the slot-scoped target
|
|
27
30
|
|
|
28
31
|
Human output: static device list prints immediately; live-state lines append
|
|
29
32
|
after the ~2s probe window (screen, wallet, account, fixture per device).
|
|
33
|
+
By default mobile status shows the configured target only; --device <id>
|
|
34
|
+
switches the active target for that command. Other devices that answer on the
|
|
35
|
+
same Metro are reported as additional reachable targets.
|
|
30
36
|
|
|
31
37
|
The devices[] section carries per device:
|
|
32
38
|
{ platform, id, name, state, selected } always present
|
|
@@ -168,6 +174,7 @@ Example:
|
|
|
168
174
|
--expect-live Exit 0 iff the runtime is live (extension: watcher+CDP; mobile: Metro+bridge; core: deps), non-zero + teaching escape otherwise
|
|
169
175
|
--cdp-port <port> Extension CDP port for the liveness probe (env: CDP_PORT / RECIPE_CDP_PORT)
|
|
170
176
|
--device <udid|serial|name> Mobile only: target this device (env: IOS_SIMULATOR / ADB_SERIAL). doctor reports the connected devices; it never gates on ambiguity.
|
|
177
|
+
--all-devices Mobile only: show every connected device instead of the slot-scoped target
|
|
171
178
|
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
172
179
|
--target <path> Checkout path (default: cwd)
|
|
173
180
|
--runtime-dir <dir> Runtime dir containing agentic-runtime.json (relative to target)
|
|
@@ -179,6 +186,27 @@ Example:
|
|
|
179
186
|
mm-harness doctor --adapter extension --target /path/to/checkout --cdp-port 6662 --expect-live
|
|
180
187
|
mm-harness doctor --adapter mobile --target /path/to/checkout`
|
|
181
188
|
},
|
|
189
|
+
{
|
|
190
|
+
name: "check",
|
|
191
|
+
summary: "Run bounded repo-local checks for an active git diff and write validation artifacts.",
|
|
192
|
+
example: "mm-harness check diff --profile fast",
|
|
193
|
+
helpText: `mm-harness check diff [flags]
|
|
194
|
+
|
|
195
|
+
Run bounded repo-local checks for the active git diff. Fast profile runs changed-file
|
|
196
|
+
ESLint, Prettier, and changed test files. Full profile also runs repo typecheck
|
|
197
|
+
when available. Does not launch an app or run a recipe.
|
|
198
|
+
|
|
199
|
+
--target <path> Checkout path (default: cwd)
|
|
200
|
+
--adapter <mobile|extension|core> Adapter label (auto-detected when possible)
|
|
201
|
+
--base <ref> Diff base (default: PR base, then remote HEAD, then repo fallback)
|
|
202
|
+
--profile <fast|full> Validation depth (default: fast)
|
|
203
|
+
--artifacts-dir <dir> Write validation-summary.json/.md and per-check logs
|
|
204
|
+
--json Machine-readable envelope
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
mm-harness check diff --profile fast --artifacts-dir artifacts/validation
|
|
208
|
+
mm-harness check diff --profile full --json`
|
|
209
|
+
},
|
|
182
210
|
{
|
|
183
211
|
name: "recipe-quality",
|
|
184
212
|
summary: "Build the recipe-quality artifact from a compact verdict JSON (validates before writing).",
|
|
@@ -188,7 +216,12 @@ Example:
|
|
|
188
216
|
Build artifacts/recipe-quality.json from the compact fields a recipe-quality pass
|
|
189
217
|
produces (verdict + reasons + optional guidance/dimensions/findings/delta/training).
|
|
190
218
|
The artifact is validated against the RecipeQualityArtifact schema before it is
|
|
191
|
-
written \u2014 an invalid input never reaches disk.
|
|
219
|
+
written \u2014 an invalid input never reaches disk. Shorthand without "build" is
|
|
220
|
+
also accepted when --input/--output are present.
|
|
221
|
+
|
|
222
|
+
This CLI does not run the recipe critique itself; use the fs-recipe-quality
|
|
223
|
+
skill to judge recipe/evidence quality, then use this command to build the
|
|
224
|
+
canonical artifact from that compact verdict.
|
|
192
225
|
|
|
193
226
|
--input <compact.json> Compact verdict JSON: { "verdict": "pass|warn|fail", "reasons": [..],
|
|
194
227
|
"betterVersionGuidance"?: [..], "dimensions"?: {..}, "trainingFields"?: {..}, \u2026 }
|
|
@@ -199,6 +232,7 @@ Example:
|
|
|
199
232
|
|
|
200
233
|
Example:
|
|
201
234
|
mm-harness recipe-quality build --input compact.json --output artifacts/recipe-quality.json
|
|
235
|
+
mm-harness recipe-quality --input compact.json --output artifacts/recipe-quality.json
|
|
202
236
|
mm-harness recipe-quality build --input compact.json --output artifacts/recipe-quality.json --json`
|
|
203
237
|
},
|
|
204
238
|
{
|
|
@@ -297,7 +331,7 @@ Example:
|
|
|
297
331
|
},
|
|
298
332
|
{
|
|
299
333
|
name: "launch",
|
|
300
|
-
summary: "Launch the app
|
|
334
|
+
summary: "Launch the app and adapter dev server, auto-ensuring the runtime overlay first. Mobile: ios|android required.",
|
|
301
335
|
example: "mm-harness launch ios",
|
|
302
336
|
helpText: `mm-harness launch [ios|android] [flags]
|
|
303
337
|
|
|
@@ -312,7 +346,7 @@ Example:
|
|
|
312
346
|
--runway Post-launch runway check (mobile only; teaching error elsewhere)
|
|
313
347
|
--watch Persistent webpack watcher then relaunch (extension only)
|
|
314
348
|
--sidepanel | --fullscreen Extension display mode (default --fullscreen)
|
|
315
|
-
--url <dapp-url> Open a dapp
|
|
349
|
+
--url <dapp-url> Open a dapp beside the sidepanel (default: MetaMask test dapp)
|
|
316
350
|
--heal <off|infra-only|auto> Healing policy (default auto); bounds always enforced
|
|
317
351
|
--device <udid|name> Target simulator/device (env: IOS_SIMULATOR / ADB_SERIAL)
|
|
318
352
|
--cdp-port <port> Extension CDP port (env: CDP_PORT / RECIPE_CDP_PORT)
|
|
@@ -424,14 +458,6 @@ Example:
|
|
|
424
458
|
mm-harness fixtures finalize --fixture wallet-fixture.json --state fixture-state.json --cdp-port 6661 --extension-dir dist/chrome`
|
|
425
459
|
}
|
|
426
460
|
];
|
|
427
|
-
const RETIRED_INTERNAL = [
|
|
428
|
-
"runtime-health",
|
|
429
|
-
"runtime-decision",
|
|
430
|
-
"runtime-launch",
|
|
431
|
-
"resolve-extension",
|
|
432
|
-
"ensure-ready",
|
|
433
|
-
"self-test"
|
|
434
|
-
];
|
|
435
461
|
const RETIRED = [
|
|
436
462
|
{
|
|
437
463
|
name: "live",
|
|
@@ -445,14 +471,7 @@ Replacement: mm-harness launch --verify (install overlay \u2192 launch \u2192 CD
|
|
|
445
471
|
|
|
446
472
|
Replacement: mm-harness actions --raw (works now \u2014 dumps the raw action registry JSON,
|
|
447
473
|
identical to the old \`manifest --json\`). Manifest validation moved into doctor / run --plan.`
|
|
448
|
-
}
|
|
449
|
-
...RETIRED_INTERNAL.map((name) => ({
|
|
450
|
-
name,
|
|
451
|
-
message: `mm-harness ${name} is retired (exit 2).
|
|
452
|
-
|
|
453
|
-
It is internal now \u2014 its logic lives inside doctor / launch / verify self-healing.
|
|
454
|
-
Use: mm-harness doctor`
|
|
455
|
-
}))
|
|
474
|
+
}
|
|
456
475
|
];
|
|
457
476
|
const HELP_GROUPS = [
|
|
458
477
|
{
|
|
@@ -468,7 +487,7 @@ const HELP_GROUPS = [
|
|
|
468
487
|
{
|
|
469
488
|
title: "PROVE",
|
|
470
489
|
blurb: "run recipes and inspect readiness",
|
|
471
|
-
commands: ["run", "doctor", "recipe-quality"]
|
|
490
|
+
commands: ["run", "doctor", "check", "recipe-quality"]
|
|
472
491
|
},
|
|
473
492
|
{
|
|
474
493
|
title: "RUNTIME OVERLAY",
|
|
@@ -486,6 +505,14 @@ function commandMeta(name) {
|
|
|
486
505
|
if (real) return { summary: real.summary, example: real.example, planned: false };
|
|
487
506
|
return { summary: "", example: "", planned: false };
|
|
488
507
|
}
|
|
508
|
+
function adapterFromRuntimeContext(ctx) {
|
|
509
|
+
const raw = typeof ctx.platform === "string" ? ctx.platform : void 0;
|
|
510
|
+
if (!raw) return void 0;
|
|
511
|
+
if (raw === "mobile" || raw === "ios" || raw === "android") return "mobile";
|
|
512
|
+
if (raw === "extension" || raw === "chrome-extension") return "extension";
|
|
513
|
+
if (raw === "core") return "core";
|
|
514
|
+
return void 0;
|
|
515
|
+
}
|
|
489
516
|
function detectedSlotLine(out) {
|
|
490
517
|
const ctxPath = path.join(
|
|
491
518
|
process.cwd(),
|
|
@@ -494,10 +521,14 @@ function detectedSlotLine(out) {
|
|
|
494
521
|
);
|
|
495
522
|
try {
|
|
496
523
|
const ctx = JSON.parse(fs.readFileSync(ctxPath, "utf8"));
|
|
524
|
+
const adapter = adapterFromRuntimeContext(ctx) ?? detectAdapter(process.cwd());
|
|
497
525
|
const parts = [];
|
|
498
526
|
if (ctx.slotId) parts.push(`slot ${out("ok", String(ctx.slotId))}`);
|
|
499
527
|
if (ctx.simulator) parts.push(`device ${out("ok", String(ctx.simulator))}`);
|
|
500
|
-
|
|
528
|
+
const devServerPort = ctx.watcherPort ?? ctx.devServerPort ?? ctx.metroPort;
|
|
529
|
+
if (adapter && adapter !== "core" && devServerPort) {
|
|
530
|
+
parts.push(`${getAdapterSurface(adapter).devServer.label} :${out("ok", String(devServerPort))}`);
|
|
531
|
+
}
|
|
501
532
|
if (ctx.gitBranch) parts.push(`branch ${out("info", String(ctx.gitBranch))}`);
|
|
502
533
|
if (parts.length === 0) return null;
|
|
503
534
|
return `${out("label", "SLOT")} \u2014 this checkout is a prepared slot: ${parts.join(" \xB7 ")}`;
|
|
@@ -582,7 +613,15 @@ for (const command of REAL) {
|
|
|
582
613
|
process.exit(await delegate(argv));
|
|
583
614
|
});
|
|
584
615
|
}
|
|
585
|
-
const HIDDEN = [
|
|
616
|
+
const HIDDEN = [
|
|
617
|
+
"completion-candidates",
|
|
618
|
+
"runtime-health",
|
|
619
|
+
"runtime-decision",
|
|
620
|
+
"runtime-launch",
|
|
621
|
+
"resolve-extension",
|
|
622
|
+
"ensure-ready",
|
|
623
|
+
"self-test"
|
|
624
|
+
];
|
|
586
625
|
for (const name of HIDDEN) {
|
|
587
626
|
program.command(name, { hidden: true }).allowUnknownOption().helpOption(false).argument("[args...]").action(async () => {
|
|
588
627
|
process.exit(await delegate(rawArgv));
|
package/docs/CLI-SPEC.md
CHANGED
|
@@ -54,6 +54,7 @@ Name `metamask-recipe` → **REMOVE** (becomes `mm-harness`; no alias). Capabili
|
|
|
54
54
|
| `resolve-extension` | Resolve the extension id | extension | advanced | **KEEP-INTERNAL** — internal plumbing; called inside `launch`/`launch --verify` |
|
|
55
55
|
| `ensure-ready` | Ensure extension ready (close home tabs) | extension | advanced | **KEEP-INTERNAL** — becomes internal function inside `launch`/`verify` self-healing path |
|
|
56
56
|
| `run` | Run a recipe, write evidence | all | PROVE | ROUTES-NOW |
|
|
57
|
+
| `check diff` | Run bounded repo-local validation for the active git diff | all | PROVE | ROUTES-NOW |
|
|
57
58
|
| `self-test` | Package-wiring smoke (no device) | mobile+extension | advanced | **KEEP-INTERNAL** — CI smoke test; callable via `package.json` scripts; hidden from `--help` |
|
|
58
59
|
| `flows` | List/promote library flows | all | DISCOVER | ROUTES-NOW |
|
|
59
60
|
| `harness install\|verify\|cleanup\|live` | Runtime overlay lifecycle | all (core: no live) | OVERLAY | ROUTES-NOW (`harness` prefix → top-level); `live` **REMOVE** — dissolved into `launch --verify` |
|
|
@@ -437,6 +438,30 @@ Readiness check for a checkout without launching the app. Doctor is the single p
|
|
|
437
438
|
|
|
438
439
|
---
|
|
439
440
|
|
|
441
|
+
## `check diff` (REAL → bounded local validation)
|
|
442
|
+
|
|
443
|
+
**Synopsis:** `mm-harness check diff [flags]`
|
|
444
|
+
|
|
445
|
+
Runs bounded validation against the target checkout's active git diff. This is
|
|
446
|
+
for local PR readiness loops and agent templates; it does not launch an app or
|
|
447
|
+
run a recipe.
|
|
448
|
+
|
|
449
|
+
| Flag | Values | Default | Purpose |
|
|
450
|
+
| --- | --- | --- | --- |
|
|
451
|
+
| `--target <repo>` | path | cwd | MetaMask checkout to inspect |
|
|
452
|
+
| `--adapter <adapter>` / `--platform <adapter>` | mobile\|extension\|core | auto-detect | Adapter label for the report |
|
|
453
|
+
| `--base <ref>` | git ref | GitHub PR base, then remote HEAD, then `origin/develop`/`origin/main`/local fallback | Diff base |
|
|
454
|
+
| `--profile <profile>` | `fast`\|`full` | `fast` | `fast` runs changed-file ESLint/Prettier/Jest; `full` also runs repo typecheck when available |
|
|
455
|
+
| `--artifacts-dir <dir>` | path | `temp/recipe/check-diff/<timestamp>` | Writes summary and per-check logs |
|
|
456
|
+
| `--json` | bool | false | Print the summary envelope to stdout |
|
|
457
|
+
|
|
458
|
+
**Artifacts:** `validation-summary.json`, `validation-summary.md`, and one log
|
|
459
|
+
per executed check.
|
|
460
|
+
|
|
461
|
+
**Exit:** 0 pass · 2 bad args / not a git checkout · 5 validation failure.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
440
465
|
## `--device <id>` — first-class mobile device targeting (REAL)
|
|
441
466
|
|
|
442
467
|
`--device` is the uniform mobile device selector on `run`, `call`, `doctor`, and `fixtures`. Pass the **adb serial** for Android (from `adb devices`) or the **UDID / simulator name** for iOS. The harness resolves the adb serial to the Metro CDP target identity internally — users never need to know or set `ANDROID_DEVICE='Pixel 6 - 16 - API 36'`.
|
|
@@ -46,14 +46,17 @@ function cdpPort(input) {
|
|
|
46
46
|
return port;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function extensionTarget(targets, extensionId = null) {
|
|
50
|
-
|
|
49
|
+
export function extensionTarget(targets, extensionId = null) {
|
|
50
|
+
const candidates = (Array.isArray(targets) ? targets : []).filter((target) => {
|
|
51
51
|
if (target?.type !== 'page') return false;
|
|
52
52
|
if (!String(target?.url ?? '').startsWith('chrome-extension://')) return false;
|
|
53
|
-
if (!String(target.url).includes('/home.html')) return false;
|
|
53
|
+
if (!String(target.url).includes('/home.html') && !String(target.url).includes('/sidepanel.html')) return false;
|
|
54
54
|
if (extensionId && extensionIdFromTarget(target) !== extensionId) return false;
|
|
55
55
|
return Boolean(target.webSocketDebuggerUrl);
|
|
56
|
-
})
|
|
56
|
+
});
|
|
57
|
+
return candidates.find((target) => String(target.url).includes('/home.html')) ??
|
|
58
|
+
candidates.find((target) => String(target.url).includes('/sidepanel.html')) ??
|
|
59
|
+
null;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@farmslot/agent-runtime": "^0.1.
|
|
20
|
-
"@farmslot/protocol": "^0.7.
|
|
21
|
-
"@farmslot/recipe-harness": "^0.4.
|
|
19
|
+
"@farmslot/agent-runtime": "^0.1.1",
|
|
20
|
+
"@farmslot/protocol": "^0.7.6",
|
|
21
|
+
"@farmslot/recipe-harness": "^0.4.3",
|
|
22
22
|
"commander": "^12.0.0",
|
|
23
23
|
"viem": "^2.54.3"
|
|
24
24
|
},
|