@deeeed/metamask-harness 0.83.0 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +8 -1
  3. package/adapters/manifest.json +14 -110
  4. package/adapters/shared/session-lock.mjs +43 -0
  5. package/adapters/web-dapp/metamask-signer.mjs +212 -0
  6. package/dist/adapters/extension/run.js +4 -2
  7. package/dist/adapters/extension/runtime.js +71 -18
  8. package/dist/adapters/mobile/devices.js +5 -1
  9. package/dist/adapters/mobile/run.js +1 -1
  10. package/dist/adapters/mobile/surface.js +1 -2
  11. package/dist/adapters/plugin-surface.js +14 -42
  12. package/dist/adapters/session-lock.js +23 -0
  13. package/dist/adapters/web-dapp/console-capture.js +2 -20
  14. package/dist/adapters/web-dapp/run.js +25 -24
  15. package/dist/adapters/web-dapp/surface.js +32 -108
  16. package/dist/command-contract.js +1342 -643
  17. package/dist/commands/doctor.js +1 -1
  18. package/dist/commands/launch/device.js +1 -1
  19. package/dist/doctor.js +5 -2
  20. package/dist/mm-harness-cli.js +228 -1210
  21. package/docs/CONTRIBUTING.md +6 -4
  22. package/docs/cli-contract.md +1 -1
  23. package/docs/feature-guard.md +1 -1
  24. package/library/actions/shared/console-collector.mjs +1 -1
  25. package/library/actions/web-dapp/app/launch.mjs +1 -1
  26. package/library/actions/web-dapp/platform/adapter-web-lib.mjs +18 -0
  27. package/library/actions/web-dapp/platform/hud.mjs +1 -1
  28. package/library/actions/web-dapp/platform/page.mjs +2 -3
  29. package/library/actions/web-dapp/platform/runtime.mjs +29 -178
  30. package/package.json +7 -7
  31. package/scripts/completions.sh +1 -1
  32. package/adapters/web-dapp/cleanup.mjs +0 -69
  33. package/adapters/web-dapp/inject.mjs +0 -75
  34. package/adapters/web-dapp/launch.mjs +0 -631
  35. package/adapters/web-dapp/lib/blocked-hosts.mjs +0 -24
  36. package/adapters/web-dapp/lib/cdp-client.mjs +0 -84
  37. package/adapters/web-dapp/lib/display.mjs +0 -115
  38. package/adapters/web-dapp/lib/hud.mjs +0 -148
  39. package/adapters/web-dapp/lib/policy-fence.mjs +0 -119
  40. package/adapters/web-dapp/lib/policy.mjs +0 -84
  41. package/adapters/web-dapp/lib/processes.mjs +0 -187
  42. package/adapters/web-dapp/lib/readiness.mjs +0 -272
  43. package/adapters/web-dapp/stop.mjs +0 -38
  44. package/adapters/web-dapp/verify.mjs +0 -51
  45. package/adapters/web-dapp/wallet-host.mjs +0 -520
  46. package/dist/adapters/web-dapp/doctor-checks.js +0 -33
  47. package/dist/adapters/web-dapp/launch.js +0 -87
  48. package/dist/adapters/web-dapp/overlay.js +0 -21
  49. package/dist/adapters/web-dapp/readiness.js +0 -10
  50. package/dist/adapters/web-dapp/stop.js +0 -20
  51. package/dist/cli.js +0 -202
@@ -1,10 +0,0 @@
1
- import { POLICY_ENV, webDappPolicy } from "../../../adapters/web-dapp/lib/policy.mjs";
2
- const webDappReadiness = {
3
- compatibilityMode: (target) => process.env[POLICY_ENV] && webDappPolicy().checkout.matches(target) ? "slot browser over CDP (website; no product bridge)" : "unsupported/no bridge",
4
- liveChecks: async (target) => (await import("./doctor-checks.js")).webDappDoctorChecks(target),
5
- // Screenshots come from the slot browser over CDP.
6
- captureProviders: ["cdp"]
7
- };
8
- export {
9
- webDappReadiness
10
- };
@@ -1,20 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import path from "node:path";
3
- import { runnerDir } from "../../paths.js";
4
- import { webDappAdapterId } from "../../../adapters/web-dapp/lib/policy.mjs";
5
- function stopWebDappBrowserSync(target) {
6
- const args = [path.join(runnerDir, "adapters", "web-dapp", "stop.mjs"), "--target", path.resolve(target)];
7
- const cdpPort = process.env.RECIPE_CDP_PORT ?? process.env.CDP_PORT;
8
- if (cdpPort) args.push("--cdp-port", cdpPort);
9
- const result = spawnSync(process.execPath, args, { encoding: "utf8" });
10
- const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
11
- const status = result.status ?? 1;
12
- return {
13
- status,
14
- summary: status === 0 ? `${webDappAdapterId()} browser and wallet host stopped` : `${webDappAdapterId()} browser stop failed`,
15
- output
16
- };
17
- }
18
- export {
19
- stopWebDappBrowserSync
20
- };
package/dist/cli.js DELETED
@@ -1,202 +0,0 @@
1
- import "./host.js";
2
- import "./adapters/surface.js";
3
- import fs from "node:fs";
4
- import {
5
- detectAdapter,
6
- handleActions,
7
- handleCall,
8
- handleHarness,
9
- handleLast,
10
- handleLaunch,
11
- handleReload,
12
- handleRun,
13
- handleStop,
14
- parseArgs,
15
- targetPath
16
- } from "@farmslot/recipe-cli/harness";
17
- import { stopProjectCollector } from "../library/actions/shared/analytics/collector.mjs";
18
- import { loadSelectedAdapter } from "./adapters/plugin-surface.js";
19
- import { adapterRegistry } from "./adapters/registry.js";
20
- import { applyDeviceTargeting } from "./commands/device-target.js";
21
- import { metaMaskEngine } from "./engine.js";
22
- import { walletFixturePath } from "./paths.js";
23
- import { handleDoctor } from "./commands/doctor.js";
24
- import { handleProvision } from "./commands/provision.js";
25
- import { handleRuntimeHealth } from "./commands/runtime-health.js";
26
- import { handleRuntimeLaunch } from "./commands/runtime-launch.js";
27
- import { handleRuntimeDecision } from "./commands/runtime-decision.js";
28
- import { handleResolveExtension } from "./commands/resolve-extension.js";
29
- import { handleEnsureReady } from "./commands/ensure-ready.js";
30
- import { handleSelfTest } from "./commands/self-test.js";
31
- import { handleCompletionCandidates, invalidateCompletionCache } from "./commands/completion-candidates.js";
32
- import { handleLogs } from "./commands/logs.js";
33
- import { handleDebug } from "./commands/debug.js";
34
- import { handleFixtures } from "./commands/fixtures.js";
35
- import { handleRecipeQuality } from "./commands/recipe-quality.js";
36
- import { handleStatus } from "./commands/status.js";
37
- import { handleCheck } from "./commands/check.js";
38
- import { handleChecklist } from "./commands/checklist.js";
39
- import { handleExecutionTemplate } from "./commands/execution-template.js";
40
- import { handlePrepare } from "./commands/prepare.js";
41
- import { handleTaskInit } from "./commands/task-init.js";
42
- import { handleReview } from "./commands/review.js";
43
- import { handleDomain } from "./commands/domain.js";
44
- import { handleConfig } from "./commands/config.js";
45
- import { handlePrBody } from "./commands/pr-body.js";
46
- import { overlayCommandOptions } from "./commands/overlay.js";
47
- const stopOptions = {
48
- companions: async (target) => {
49
- const collector = await stopProjectCollector(target);
50
- return collector.stopped && collector.pid !== null ? [{ label: "analytics collector", pid: collector.pid }] : [];
51
- }
52
- };
53
- const targetDevice = (command, adapter, options) => applyDeviceTargeting(command, adapter, options, { gate: true, rerun: "" });
54
- const runOptions = {
55
- engine: metaMaskEngine,
56
- targetDevice,
57
- plan: {
58
- // The wallet fixture `fixtures set` seeds.
59
- steps: (target) => [{
60
- step: "fixture.file",
61
- confidence: "static",
62
- status: "ok",
63
- detail: fs.existsSync(walletFixturePath(target)) ? `present: ${walletFixturePath(target)}` : `absent: ${walletFixturePath(target)} (fixtures set to seed)`
64
- }],
65
- launchDetail: "would launch/attach the app + heal transport (Metro/Chrome/CDP) before executing"
66
- }
67
- };
68
- const callOptions = {
69
- engine: metaMaskEngine,
70
- targetDevice,
71
- // A wallet action makes the most useful first example.
72
- exampleAction: (names) => names.find((name) => /wallet|unlock/u.test(name))
73
- };
74
- const COMMANDS = {
75
- actions: (parsed) => handleActions(parsed, { catalog: metaMaskEngine }),
76
- doctor: handleDoctor,
77
- "runtime-health": handleRuntimeHealth,
78
- "runtime-decision": handleRuntimeDecision,
79
- "runtime-launch": handleRuntimeLaunch,
80
- "resolve-extension": handleResolveExtension,
81
- "ensure-ready": handleEnsureReady,
82
- "self-test": handleSelfTest
83
- };
84
- const OVERLAY_COMMANDS = ["install", "verify", "cleanup"];
85
- function usage() {
86
- console.error(`mm-harness \u2014 the MetaMask recipe harness: launch the app, prove behavior, manage the runtime overlay.
87
- Run it from inside a MetaMask checkout; the platform (mobile | extension | core | terminal) is auto-detected.
88
- Grammar: mm-harness <command> [target] [flags] (target is a positional: ios | android | extension; flags add agent depth)
89
-
90
- DAILY LOOP \u2014 what a teammate runs many times a day:
91
- launch Launch the app incl. Metro/build, surface auto-detected. Mobile: ios|android required.
92
- mm-harness launch ios # quick relaunch (add --build | --verify)
93
- logs Tail Metro/webpack + app logs.
94
- mm-harness logs
95
- debug Open the debug console (extension DevTools / mobile RN).
96
- mm-harness debug
97
- reload Reload the connected Mobile or Extension runtime.
98
- mm-harness reload
99
- fixtures Sync files, set/reset the wallet, generate fixture-state, or finalize labels over CDP.
100
- mm-harness fixtures sync # or: set | reset | generate --fixture <f> --out <o> | finalize \u2026
101
-
102
- PROVE \u2014 run recipes and inspect capabilities:
103
- run Run a recipe and write evidence (summary/trace/artifacts).
104
- mm-harness run recipe.json
105
- doctor Readiness check for a checkout (no app launch).
106
- mm-harness doctor
107
- check Run bounded repo-local checks for an active diff.
108
- mm-harness check diff --profile fast --artifacts-dir artifacts/validation
109
- actions Describe the actions a manifest declares.
110
- mm-harness actions --adapter mobile
111
- RUNTIME OVERLAY \u2014 install/verify/clean the per-checkout runtime overlay:
112
- provision Install the cached Runway mobile dev client (artifact cache + sim install).
113
- mm-harness provision runway ios --adapter mobile
114
- install Install the runtime overlay into the checkout.
115
- mm-harness install # inside a checkout, auto-detected
116
- verify Check the overlay/runtime is present and healthy (no launch).
117
- mm-harness verify
118
- cleanup Remove the installed overlay and restore the checkout.
119
- mm-harness cleanup
120
-
121
- ADVANCED \u2014 internal runtime probes (rarely typed by hand):
122
- runtime-health runtime-decision runtime-launch
123
- resolve-extension ensure-ready self-test
124
- (e.g. mm-harness runtime-decision --adapter extension --target <repo> --json)
125
-
126
- ONE bin: mm-harness is the only command. No per-platform binaries \u2014 platform is
127
- auto-detected, the positional target forces it (mm-harness launch ios), and
128
- platform-specific needs are FLAGS on the same command (e.g. --sidebar, --full-build).
129
- Human happy path = the bare command; agents add depth via flags (--json, --target, ports).
130
- See README.md for the workflow and docs/RECIPES.md for proof authoring.
131
- `);
132
- }
133
- async function main(argv) {
134
- const command = argv[0];
135
- if (!command || command === "-h" || command === "--help") {
136
- usage();
137
- return command ? 0 : 2;
138
- }
139
- const refused = await loadSelectedAdapter(command, argv.slice(1));
140
- if (refused !== void 0) return refused;
141
- if (command === "status" || command === "health" || command === "home") {
142
- return handleStatus(parseArgs(argv.slice(1)));
143
- }
144
- if (command === "run") return handleRun(argv.slice(1), runOptions);
145
- if (command === "call") return handleCall(argv.slice(1), callOptions);
146
- if (command === "completion-candidates") return handleCompletionCandidates(argv.slice(1));
147
- if (command === "provision") return handleProvision(parseArgs(argv.slice(1)));
148
- if (OVERLAY_COMMANDS.includes(command)) {
149
- if (command === "verify" && process.env.MM_HARNESS_SKIP_OVERLAY_ENSURE !== "1") {
150
- try {
151
- const { options } = parseArgs(argv.slice(1));
152
- const target = targetPath(options);
153
- const adapterRaw = (typeof options["adapter"] === "string" ? options["adapter"] : void 0) ?? (typeof options["platform"] === "string" ? options["platform"] : void 0) ?? detectAdapter(target);
154
- if (adapterRaw && adapterRegistry.has(adapterRaw)) {
155
- await handleHarness(["install", "--platform", adapterRaw, "--target", target, "--quiet"]);
156
- }
157
- } catch {
158
- }
159
- }
160
- const code = await handleHarness(argv, overlayCommandOptions);
161
- if (code === 0 && command === "install") {
162
- try {
163
- invalidateCompletionCache(targetPath(parseArgs(argv.slice(1)).options));
164
- } catch {
165
- }
166
- }
167
- return code;
168
- }
169
- if (command === "launch") return handleLaunch(argv.slice(1));
170
- if (command === "stop") return handleStop(argv.slice(1), stopOptions);
171
- if (command === "logs") return handleLogs(argv.slice(1));
172
- if (command === "debug") return handleDebug(argv.slice(1));
173
- if (command === "reload") return handleReload(argv.slice(1));
174
- if (command === "fixtures") return handleFixtures(argv.slice(1));
175
- if (command === "recipe-quality") return handleRecipeQuality(argv.slice(1));
176
- if (command === "check") return handleCheck(argv.slice(1));
177
- if (command === "checklist") return handleChecklist(argv.slice(1));
178
- if (command === "execution-template") return handleExecutionTemplate(argv.slice(1));
179
- if (command === "task") return handleTaskInit(argv.slice(1));
180
- if (command === "prepare") return handlePrepare(argv.slice(1));
181
- if (command === "last") return handleLast(parseArgs(argv.slice(1)));
182
- if (command === "review") return handleReview(argv.slice(1));
183
- if (command === "domain") return handleDomain(argv.slice(1));
184
- if (command === "config") return handleConfig(argv.slice(1));
185
- if (command === "pr-body") return handlePrBody(argv.slice(1));
186
- const handler = COMMANDS[command];
187
- if (!handler) throw new Error(`Unknown command: ${command}`);
188
- return handler(parseArgs(argv.slice(1), command === "runtime-decision" ? { recordIsFlag: true } : {}));
189
- }
190
- if (!globalThis.__MM_HARNESS_WRAPPER__) {
191
- try {
192
- process.exit(await main(process.argv.slice(2)));
193
- } catch (error) {
194
- console.error(error instanceof Error ? error.message : String(error));
195
- process.exit(
196
- error !== null && typeof error === "object" && "exitCode" in error && typeof error.exitCode === "number" ? error.exitCode : 1
197
- );
198
- }
199
- }
200
- export {
201
- main
202
- };