@deeeed/metamask-harness 0.17.5 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +1059 -0
  2. package/README.md +91 -140
  3. package/adapters/core/inject.sh +1 -6
  4. package/adapters/extension/console-tail.mjs +55 -24
  5. package/adapters/extension/ensure-browser.sh +6 -1
  6. package/adapters/extension/inject.mjs +7 -13
  7. package/adapters/extension/launch-browser.cjs +7 -1
  8. package/adapters/extension/launch-webpack.cjs +83 -0
  9. package/adapters/extension/launch.sh +8 -11
  10. package/adapters/extension/lib/chrome-args.cjs +8 -1
  11. package/adapters/extension/lib/macos-focus.cjs +32 -0
  12. package/adapters/extension/live.sh +12 -21
  13. package/adapters/extension/reattach.sh +1 -0
  14. package/adapters/extension/seed-fixture.sh +4 -12
  15. package/adapters/extension/sidepanel-toggle.sh +4 -1
  16. package/adapters/extension/stamp-runtime-title.cjs +58 -0
  17. package/adapters/extension/start-watch.sh +17 -4
  18. package/adapters/extension/stop-viewers.sh +1 -1
  19. package/adapters/extension/sync-webpack-dist.cjs +107 -0
  20. package/adapters/extension/verify.sh +2 -2
  21. package/adapters/manifest.json +53 -37
  22. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +16 -0
  23. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
  24. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +3 -1
  25. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
  26. package/adapters/mobile/cleanup.sh +16 -176
  27. package/adapters/mobile/inject.sh +91 -671
  28. package/adapters/mobile/launch-console-forwarder.cjs +39 -0
  29. package/adapters/mobile/open-device.sh +45 -7
  30. package/adapters/mobile/start-console-forwarder.sh +70 -0
  31. package/adapters/mobile/start-metro.sh +0 -41
  32. package/adapters/mobile/verify.sh +34 -83
  33. package/adapters/mobile/wait-for-bridge.sh +22 -8
  34. package/adapters/shared/harness-source-fingerprint.mjs +14 -12
  35. package/adapters/shared/resolve-slot-ports-core.mjs +17 -6
  36. package/adapters/shared/resolve-slot-ports.sh +17 -5
  37. package/adapters/shared/update-check-worker.mjs +43 -0
  38. package/bin/mm-harness +21 -2
  39. package/dist/adapters/core/surface.js +4 -1
  40. package/dist/adapters/extension/console-capture.js +95 -0
  41. package/dist/adapters/extension/product-config.js +110 -0
  42. package/dist/adapters/extension/runtime-decision.js +20 -2
  43. package/dist/adapters/extension/surface.js +22 -3
  44. package/dist/adapters/harness-freshness.js +49 -0
  45. package/dist/adapters/mobile/perps-env.js +43 -3
  46. package/dist/adapters/mobile/prepare.js +39 -7
  47. package/dist/adapters/mobile/surface.js +5 -2
  48. package/dist/adapters/resolve-slot-ports.js +2 -2
  49. package/dist/adapters/slot-ports.js +13 -16
  50. package/dist/adapters.js +56 -16
  51. package/dist/checkout-lock.js +27 -2
  52. package/dist/cli-color.js +19 -0
  53. package/dist/cli-commands.js +1 -1
  54. package/dist/cli.js +7 -18
  55. package/dist/command-contract.js +462 -0
  56. package/dist/command-journal.js +263 -0
  57. package/dist/commands/call.js +252 -57
  58. package/dist/commands/check.js +10 -4
  59. package/dist/commands/completion-candidates.js +17 -22
  60. package/dist/commands/device-target.js +27 -12
  61. package/dist/commands/doctor.js +106 -25
  62. package/dist/commands/fixtures.js +142 -35
  63. package/dist/commands/last.js +60 -0
  64. package/dist/commands/launch/extension.js +40 -17
  65. package/dist/commands/launch/index.js +207 -63
  66. package/dist/commands/launch/mobile.js +2 -0
  67. package/dist/commands/list-executables.js +187 -29
  68. package/dist/commands/logs.js +8 -6
  69. package/dist/commands/manifest.js +286 -35
  70. package/dist/commands/parse-args.js +68 -4
  71. package/dist/commands/provision.js +10 -4
  72. package/dist/commands/run-engine.js +772 -264
  73. package/dist/commands/run-report.js +12 -3
  74. package/dist/commands/run.js +378 -85
  75. package/dist/commands/shared.js +76 -4
  76. package/dist/commands/status-probe.js +4 -1
  77. package/dist/commands/status.js +2 -1
  78. package/dist/commands/stop.js +7 -2
  79. package/dist/commands/update.js +16 -28
  80. package/dist/completions-cache.js +1 -1
  81. package/dist/doctor.js +57 -10
  82. package/dist/harness.js +47 -137
  83. package/dist/heal-bounds.js +7 -2
  84. package/dist/json-stream.js +57 -0
  85. package/dist/live-adapter-contract.js +138 -32
  86. package/dist/manifest.js +161 -1
  87. package/dist/mm-harness-cli.js +146 -73
  88. package/dist/paths.js +2 -5
  89. package/dist/recipe-security.js +178 -0
  90. package/dist/run-diagnostics.js +261 -0
  91. package/dist/run-recording.js +1 -1
  92. package/dist/runner.js +140 -9
  93. package/docs/CONTRIBUTING.md +136 -0
  94. package/docs/QA.md +184 -0
  95. package/docs/RECIPES.md +122 -0
  96. package/docs/SECURITY.md +88 -0
  97. package/library/README.md +7 -3
  98. package/library/actions/core/perps/_controller.mjs +43 -56
  99. package/library/actions/core/perps/assert_orders.mjs +6 -7
  100. package/library/actions/core/perps/assert_positions.mjs +6 -7
  101. package/library/actions/core/perps/close_orders.mjs +2 -0
  102. package/library/actions/core/perps/close_positions.mjs +2 -0
  103. package/library/actions/core/perps/ensure_orders.mjs +4 -2
  104. package/library/actions/core/perps/ensure_positions.mjs +4 -2
  105. package/library/actions/core/perps/place_order.mjs +7 -3
  106. package/library/actions/core/perps/read_account.mjs +2 -2
  107. package/library/actions/core/perps/read_orders.mjs +2 -1
  108. package/library/actions/core/perps/read_positions.mjs +2 -1
  109. package/library/actions/core/wallet/list_accounts.mjs +95 -0
  110. package/library/actions/extension/perps/assert_orders.mjs +2 -1
  111. package/library/actions/extension/perps/assert_positions.mjs +2 -1
  112. package/library/actions/extension/perps/perps.mjs +43 -14
  113. package/library/actions/extension/platform/cdp.mjs +1 -0
  114. package/library/actions/extension/wallet/list_accounts.mjs +41 -0
  115. package/library/actions/mobile/perps/assert_orders.mjs +2 -1
  116. package/library/actions/mobile/perps/assert_positions.mjs +2 -1
  117. package/library/actions/mobile/perps/perps.mjs +40 -12
  118. package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
  119. package/library/library.json +1 -1
  120. package/library/manifests/core.action-manifest.json +1214 -396
  121. package/library/manifests/extension.action-manifest.json +1635 -728
  122. package/library/manifests/mobile.action-manifest.json +1740 -727
  123. package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
  124. package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
  125. package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
  126. package/library/recipes/perps/lifecycle.recipe.json +136 -180
  127. package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
  128. package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
  129. package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
  130. package/library/recipes/perps/performance.mobile.recipe.json +37 -51
  131. package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
  132. package/library/recipes/perps/read-markets.core.recipe.json +29 -31
  133. package/library/recipes/perps/smoke.core.recipe.json +29 -32
  134. package/library/recipes/perps/smoke.extension.recipe.json +41 -44
  135. package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
  136. package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
  137. package/library/recipes/runner/action-validation.extension.recipe.json +312 -398
  138. package/library/recipes/runner/action-validation.mobile.recipe.json +316 -402
  139. package/library/recipes/runner/smoke.core.recipe.json +25 -0
  140. package/library/recipes/runner/smoke.extension.recipe.json +23 -24
  141. package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
  142. package/library/recipes/wallet/smoke.extension.recipe.json +40 -0
  143. package/library/recipes/wallet/smoke.mobile.recipe.json +40 -0
  144. package/package.json +12 -5
  145. package/scripts/completions.sh +8 -11
  146. package/scripts/validate-human-outcomes.mjs +169 -0
  147. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
  148. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
  149. package/adapters/mobile/overlay/compat/README.md +0 -28
  150. package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
  151. package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
  152. package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
  153. package/adapters/shared/resolve-farmslot-ports.sh +0 -7
  154. package/dist/adapters/extension/harness-freshness.js +0 -39
  155. package/dist/commands/flows.js +0 -62
  156. package/docs/ADAPTER-SURFACE.md +0 -119
  157. package/docs/CHEATSHEET.md +0 -61
  158. package/docs/CLI-SPEC.md +0 -1098
  159. package/docs/CODE-MAP.md +0 -62
  160. package/docs/DEBUG-HANDOVER.md +0 -36
  161. package/docs/MENTAL-MODEL.md +0 -295
  162. package/docs/UX-PRINCIPLES.md +0 -64
  163. package/docs/architecture.md +0 -398
  164. package/docs/live-adapter-contract.md +0 -188
  165. package/docs/package-boundaries.md +0 -47
  166. package/docs/perps-flow-catalog.md +0 -235
  167. package/docs/recipe-libraries.md +0 -301
  168. package/docs/runtime-file-conventions.md +0 -36
  169. package/library/flows/perps.flows.json +0 -64
@@ -0,0 +1,60 @@
1
+ import { readCommandJournal } from "../command-journal.js";
2
+ import { EXIT } from "./shared.js";
3
+ import {
4
+ optionFlag,
5
+ optionString,
6
+ shellQuote,
7
+ targetPath
8
+ } from "./parse-args.js";
9
+ async function handleLast({ options }) {
10
+ const target = targetPath(options);
11
+ const { file, record } = readCommandJournal(target, optionString(options, "runtimeDir"));
12
+ const json = optionFlag(options, "json");
13
+ if (!record) {
14
+ const message = `no resumability journal exists for ${target}`;
15
+ const userAction = "run a proof or runtime command in this checkout, then re-run mm-harness last --json";
16
+ if (json) {
17
+ console.log(JSON.stringify({
18
+ schemaVersion: 1,
19
+ command: "last",
20
+ target,
21
+ journalPath: file,
22
+ status: "fail",
23
+ exitCode: EXIT.runtime,
24
+ error: { code: "LAST_NOT_FOUND", message, userAction }
25
+ }, null, 2));
26
+ } else {
27
+ console.error(`\u2717 mm-harness last: ${message}
28
+ Next: ${userAction}`);
29
+ }
30
+ return EXIT.runtime;
31
+ }
32
+ if (json) {
33
+ console.log(JSON.stringify({
34
+ schemaVersion: 1,
35
+ command: "last",
36
+ target,
37
+ journalPath: file,
38
+ status: "pass",
39
+ exitCode: EXIT.ok,
40
+ last: record
41
+ }, null, 2));
42
+ } else {
43
+ const end = record.finishedAt ?? "still running or interrupted";
44
+ console.log(`${record.verdict.toUpperCase()} ${record.command} (exit ${record.exitCode ?? "pending"})`);
45
+ console.log(`command: mm-harness ${record.command}${record.args.length > 0 ? ` ${record.args.map(shellQuote).join(" ")}` : ""}`);
46
+ console.log(`started: ${record.startedAt}`);
47
+ console.log(`finished: ${end}`);
48
+ if (record.evidencePaths.length > 0) {
49
+ console.log("evidence:");
50
+ for (const evidence of record.evidencePaths) console.log(` ${evidence}`);
51
+ } else {
52
+ console.log("evidence: none recorded");
53
+ }
54
+ console.log(`journal: ${file}`);
55
+ }
56
+ return EXIT.ok;
57
+ }
58
+ export {
59
+ handleLast
60
+ };
@@ -3,13 +3,15 @@ import http from "node:http";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { depsCheck } from "@farmslot/recipe-harness/runtime/deps-readiness";
6
+ import { colorHumanMessage } from "../../cli-color.js";
6
7
  import { recipeHarnessPath, recipeRuntimeDir, runnerDir } from "../../paths.js";
7
8
  import { extensionIdFromKey } from "../../adapters/extension/extension-id.js";
9
+ import { extensionProductConfigBlock } from "../../adapters/extension/product-config.js";
8
10
  import { isExtensionDistStale } from "../../adapters/extension/runtime-decision.js";
9
11
  import { checkExtensionRuntimeHealth } from "../../adapters/extension/runtime.js";
10
- import { ensureExtensionHarnessFresh } from "../../adapters/extension/harness-freshness.js";
12
+ import { ensureHarnessFresh } from "../../adapters/harness-freshness.js";
11
13
  import { stopExtensionWatcher } from "../../adapters/slot-ports.js";
12
- import { spawnScriptStreaming } from "../shared.js";
14
+ import { EXIT, spawnScriptStreaming } from "../shared.js";
13
15
  function extensionDepsBlock(target) {
14
16
  if (!fs.existsSync(path.join(target, "package.json"))) return null;
15
17
  const deps = depsCheck(target);
@@ -30,18 +32,32 @@ function installExtensionDeps(target) {
30
32
  return spawnScriptStreaming(installScript, ["--target", target], target);
31
33
  }
32
34
  async function launchExtension(target, tier, wantWatch, displayMode = "fullscreen") {
33
- await ensureExtensionHarnessFresh(target);
35
+ if (process.env.CHROME_USER_DATA_DIR) {
36
+ process.env.CHROME_USER_DATA_DIR = path.resolve(process.env.CHROME_USER_DATA_DIR);
37
+ }
38
+ const reusable = !wantWatch && tier !== "build" && await extensionRuntimeReusable(target);
39
+ if (!reusable) {
40
+ const block = extensionProductConfigBlock(target);
41
+ if (block) {
42
+ return {
43
+ status: EXIT.infra,
44
+ output: `EXTENSION_PRODUCT_CONFIG_REQUIRED: ${block.message}
45
+ Next: ${block.userAction}`
46
+ };
47
+ }
48
+ }
49
+ await ensureHarnessFresh(target, "extension");
34
50
  if (wantWatch) {
35
51
  const startWatchSh = path.join(runnerDir, "adapters/extension/start-watch.sh");
36
52
  const watchArgs = ["--target", target];
37
53
  if (process.env.WATCHER_PORT) watchArgs.push("--watcher-port", process.env.WATCHER_PORT);
38
- console.error(`\u2192 extension watch \u2014 webpack :${process.env.WATCHER_PORT ?? "default"} (output streams below)`);
54
+ console.error(colorHumanMessage(`\u2192 extension watch \u2014 webpack :${process.env.WATCHER_PORT ?? "default"} (output streams below)`));
39
55
  return spawnScriptStreaming(startWatchSh, watchArgs, target);
40
56
  }
41
57
  if (tier === "build") {
42
58
  return extensionRebuild(target);
43
59
  }
44
- if (await extensionRuntimeReusable(target)) {
60
+ if (reusable) {
45
61
  return extensionReattach(target, displayMode);
46
62
  }
47
63
  return extensionRebuild(target);
@@ -49,12 +65,16 @@ async function launchExtension(target, tier, wantWatch, displayMode = "fullscree
49
65
  async function extensionRuntimeReusable(target) {
50
66
  const cdpPort = process.env.CDP_PORT;
51
67
  if (!cdpPort) return false;
52
- if (!cdpOwnedByExpectedRuntime(cdpPort, target)) return false;
53
- if (!await cdpVersionReachable(cdpPort)) return false;
54
- if (!await cdpHasExpectedExtensionTarget(cdpPort, target)) return false;
55
- if (!await cdpRuntimeHealthy(cdpPort, target)) return false;
56
68
  if (isExtensionDistStale(target)) return false;
57
- return true;
69
+ for (let attempt = 0; attempt < 3; attempt += 1) {
70
+ const reachable = await cdpVersionReachable(cdpPort);
71
+ const owned = reachable && cdpOwnedByExpectedRuntime(cdpPort, target);
72
+ const targetPresent = owned && await cdpHasExpectedExtensionTarget(cdpPort, target);
73
+ const healthy = targetPresent && await cdpRuntimeHealthy(cdpPort, target);
74
+ if (healthy) return true;
75
+ if (attempt < 2) await new Promise((resolve) => setTimeout(resolve, 250));
76
+ }
77
+ return false;
58
78
  }
59
79
  function expectedChromeProfile(target) {
60
80
  const profileName = process.env.RECIPE_CHROME_PROFILE_NAME || "chrome-profile";
@@ -170,12 +190,15 @@ function cdpHasExpectedExtensionTarget(port, target) {
170
190
  });
171
191
  }
172
192
  async function cdpRuntimeHealthy(port, target) {
173
- try {
174
- const report = await checkExtensionRuntimeHealth(target, Number(port), { pageMode: "home-or-sidepanel" });
175
- return report.status === "PASS";
176
- } catch {
177
- return false;
193
+ for (let attempt = 0; attempt < 3; attempt += 1) {
194
+ try {
195
+ const report = await checkExtensionRuntimeHealth(target, Number(port), { pageMode: "home-or-sidepanel" });
196
+ if (report.status === "PASS") return true;
197
+ } catch {
198
+ }
199
+ if (attempt < 2) await new Promise((resolve) => setTimeout(resolve, 250));
178
200
  }
201
+ return false;
179
202
  }
180
203
  async function extensionReattach(target, displayMode = "fullscreen") {
181
204
  const reattachScript = recipeHarnessPath(target, "extension", "scripts", "reattach.sh");
@@ -183,7 +206,7 @@ async function extensionReattach(target, displayMode = "fullscreen") {
183
206
  if (process.env.CDP_PORT) reattachArgs.push("--cdp-port", process.env.CDP_PORT);
184
207
  if (process.env.WATCHER_PORT) reattachArgs.push("--watcher-port", process.env.WATCHER_PORT);
185
208
  if (process.env.EXTENSION_START_URL) reattachArgs.push("--start-url", process.env.EXTENSION_START_URL);
186
- console.error(`\u2192 extension quick reattach \u2014 reload in place \xB7 CDP :${process.env.CDP_PORT ?? "default"} (no rebuild, no relaunch)`);
209
+ console.error(colorHumanMessage(`\u2192 extension quick reattach \u2014 reload in place \xB7 CDP :${process.env.CDP_PORT ?? "default"} (no rebuild, no relaunch)`));
187
210
  return spawnScriptStreaming(reattachScript, reattachArgs, target);
188
211
  }
189
212
  async function extensionRebuild(target) {
@@ -197,7 +220,7 @@ async function extensionRebuild(target) {
197
220
  const liveArgs = ["--target", target, "--start-watch"];
198
221
  if (process.env.CDP_PORT) liveArgs.push("--cdp-port", process.env.CDP_PORT);
199
222
  if (process.env.EXTENSION_START_URL) liveArgs.push("--start-url", process.env.EXTENSION_START_URL);
200
- console.error(`\u2192 extension quick relaunch \u2014 webpack :${process.env.WATCHER_PORT ?? "default"} \xB7 CDP :${process.env.CDP_PORT ?? "default"} (output streams below)`);
223
+ console.error(colorHumanMessage(`\u2192 extension quick relaunch \u2014 webpack :${process.env.WATCHER_PORT ?? "default"} \xB7 CDP :${process.env.CDP_PORT ?? "default"} (output streams below)`));
201
224
  const result = await spawnScriptStreaming(liveScript, liveArgs, target);
202
225
  if (result.output) {
203
226
  try {