@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
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { detectAdapter } from "../harness.js";
3
3
  import { assertAdapter, manifestPath } from "../paths.js";
4
+ import { explicitRecipeTrustOptions } from "../recipe-security.js";
4
5
  import { ADAPTER_DETECT_NEXT } from "./shared.js";
5
6
  import { EXIT } from "./shared.js";
6
7
  class CliError extends Error {
@@ -19,12 +20,15 @@ function parseArgs(argv, command) {
19
20
  const options = {};
20
21
  const booleanOptions = /* @__PURE__ */ new Set([
21
22
  "json",
23
+ "jsonStream",
22
24
  "launchExistingDist",
23
25
  "startWatch",
24
26
  "record",
25
27
  "plan",
26
28
  "list",
29
+ "describe",
27
30
  "raw",
31
+ "categories",
28
32
  "fix",
29
33
  "force",
30
34
  "resolveOnly",
@@ -34,6 +38,7 @@ function parseArgs(argv, command) {
34
38
  "allDevices",
35
39
  "help"
36
40
  ]);
41
+ const repeatableOptions = /* @__PURE__ */ new Set(["library"]);
37
42
  for (let i = 0; i < argv.length; i += 1) {
38
43
  const arg = argv[i];
39
44
  if (!arg.startsWith("--")) {
@@ -66,15 +71,56 @@ function parseArgs(argv, command) {
66
71
  continue;
67
72
  }
68
73
  if (inlineValue !== void 0) {
69
- options[key] = inlineValue;
74
+ assignOption(options, key, inlineValue, repeatableOptions);
70
75
  continue;
71
76
  }
72
77
  if (i + 1 >= argv.length) throw usageError(`Missing value for ${arg}`);
73
- options[key] = argv[i + 1];
78
+ assignOption(options, key, argv[i + 1], repeatableOptions);
74
79
  i += 1;
75
80
  }
76
81
  return { positional, options, rawArgv: [...argv] };
77
82
  }
83
+ function assignOption(options, key, value, repeatableOptions) {
84
+ if (!repeatableOptions.has(key)) {
85
+ options[key] = value;
86
+ return;
87
+ }
88
+ const existing = options[key];
89
+ if (existing === void 0) {
90
+ options[key] = [value];
91
+ return;
92
+ }
93
+ if (!Array.isArray(existing)) throw usageError(`--${key} has conflicting values.`);
94
+ options[key] = [...existing, value];
95
+ }
96
+ function parseRecipeParamAssignments(values) {
97
+ const params = {};
98
+ for (const assignment of values) {
99
+ const separator = assignment.indexOf("=");
100
+ if (separator <= 0) {
101
+ throw usageError(`recipe parameter ${JSON.stringify(assignment)} must use key=value.`);
102
+ }
103
+ const key = assignment.slice(0, separator).trim();
104
+ if (!key) throw usageError(`recipe parameter ${JSON.stringify(assignment)} has an empty key.`);
105
+ if (Object.hasOwn(params, key)) {
106
+ throw usageError(`recipe parameter ${JSON.stringify(key)} was provided more than once.`);
107
+ }
108
+ const raw = assignment.slice(separator + 1);
109
+ let value;
110
+ try {
111
+ value = JSON.parse(raw);
112
+ } catch {
113
+ value = raw;
114
+ }
115
+ Object.defineProperty(params, key, {
116
+ value,
117
+ enumerable: true,
118
+ configurable: true,
119
+ writable: true
120
+ });
121
+ }
122
+ return params;
123
+ }
78
124
  function parseRecordVideoMode(value) {
79
125
  if (value === void 0 || value === "" || value === "true") return "full-run";
80
126
  if (value === "off" || value === "false") return false;
@@ -97,6 +143,14 @@ function optionString(options, key) {
97
143
  if (typeof value !== "string") throw usageError(`--${key} requires a value.`);
98
144
  return value;
99
145
  }
146
+ function optionStrings(options, key) {
147
+ const value = options[key];
148
+ if (value === void 0) return void 0;
149
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
150
+ throw usageError(`--${key} requires a value.`);
151
+ }
152
+ return value;
153
+ }
100
154
  function optionFlag(options, key) {
101
155
  const value = options[key];
102
156
  return value === true;
@@ -127,7 +181,7 @@ function adapterOption(options) {
127
181
  return adapter;
128
182
  }
129
183
  function targetPath(options) {
130
- return path.resolve(optionString(options, "target") ?? optionString(options, "projectRoot") ?? process.cwd());
184
+ return path.resolve(optionString(options, "target") ?? process.cwd());
131
185
  }
132
186
  function actionManifestPathOption(options, adapter) {
133
187
  const configured = optionString(options, "actionManifest");
@@ -140,13 +194,21 @@ function parsePort(value, errorMessage) {
140
194
  }
141
195
  function runtimeOptionsFromCli(options) {
142
196
  const recordVideo = options.recordVideo;
197
+ const trust = explicitRecipeTrustOptions({
198
+ sourceTrust: optionString(options, "sourceTrust"),
199
+ sourceKind: optionString(options, "sourceKind"),
200
+ sourceName: optionString(options, "sourceName"),
201
+ sourceDigest: optionString(options, "sourceDigest"),
202
+ approvalDigest: optionString(options, "approvePlan")
203
+ });
143
204
  return {
144
205
  cdpPort: optionString(options, "cdpPort"),
145
206
  watcherPort: optionString(options, "watcherPort") ?? optionString(options, "metroPort"),
146
207
  launchExistingDist: optionFlag(options, "launchExistingDist"),
147
208
  slot: optionString(options, "slot"),
148
209
  validationRuntimeDir: optionString(options, "validationRuntimeDir"),
149
- recordVideo: recordVideo === "full-run" ? "full-run" : false
210
+ recordVideo: recordVideo === "full-run" ? "full-run" : false,
211
+ ...trust
150
212
  };
151
213
  }
152
214
  function resolveAdapter(options) {
@@ -184,8 +246,10 @@ export {
184
246
  isRecord,
185
247
  optionFlag,
186
248
  optionString,
249
+ optionStrings,
187
250
  parseArgs,
188
251
  parsePort,
252
+ parseRecipeParamAssignments,
189
253
  requiredOption,
190
254
  resolveAdapter,
191
255
  runtimeOptionsFromCli,
@@ -1,7 +1,7 @@
1
1
  import { detectAdapter } from "../harness.js";
2
2
  import { assertAdapter } from "../paths.js";
3
3
  import { getAdapterSurface } from "../adapters/surface.js";
4
- import { ADAPTER_DETECT_NEXT, usageOut } from "./shared.js";
4
+ import { ADAPTER_DETECT_NEXT, usageOut, writeInteractiveProgress } from "./shared.js";
5
5
  import {
6
6
  applyWatcherPortOption,
7
7
  optionFlag,
@@ -25,9 +25,13 @@ async function handleProvision({ positional, options, rawArgv }) {
25
25
  }
26
26
  const surface = getAdapterSurface(adapter);
27
27
  const rerunCommand = provisionRerunCommand(rawArgv, options, adapter, target);
28
+ const platform = optionString(options, "platform") ?? optionString(options, "devicePlatform") ?? (positional[0] === "runway" ? positional[1] : positional[0]) ?? "ios";
29
+ if (adapter === "mobile") {
30
+ writeInteractiveProgress(json, `\u2192 provision mobile ${platform} \u2014 resolving simulator and Runway artifact`);
31
+ }
28
32
  const result = await surface.runwayProvision.run(target, {
29
33
  json,
30
- platform: optionString(options, "platform") ?? optionString(options, "devicePlatform") ?? (positional[0] === "runway" ? positional[1] : positional[0]) ?? "ios",
34
+ platform,
31
35
  branch: optionString(options, "branch"),
32
36
  defaultBranch: optionString(options, "defaultBranch"),
33
37
  run: optionString(options, "run"),
@@ -42,8 +46,10 @@ async function handleProvision({ positional, options, rawArgv }) {
42
46
  resolveOnly: optionFlag(options, "resolveOnly"),
43
47
  rerunCommand
44
48
  });
49
+ const next = result.status === "pass" && adapter === "mobile" && result.resolveOnly !== true ? `mm-harness launch ${String(result.platform ?? platform)} --adapter mobile --target ${shellQuote(target)}` : void 0;
50
+ const output = next ? { ...result, next } : result;
45
51
  if (json) {
46
- console.log(JSON.stringify(result, null, 2));
52
+ console.log(JSON.stringify(output, null, 2));
47
53
  } else if (result.status === "pass") {
48
54
  const cache = typeof result.cache === "object" && result.cache ? result.cache : void 0;
49
55
  const simulator = typeof result.simulator === "object" && result.simulator ? result.simulator : void 0;
@@ -54,6 +60,7 @@ async function handleProvision({ positional, options, rawArgv }) {
54
60
  const action = result.skipped ? "already provisioned" : "provisioned";
55
61
  console.error(`\u2713 ${action} ${adapter} ${result.platform ?? ""} simulator=${simulator?.name ?? "unknown"} cache=${cache?.status ?? "unknown"}`);
56
62
  }
63
+ if (next) console.error(` Next: ${next}`);
57
64
  } else {
58
65
  console.error(`\u2717 mm-harness provision: ${result.error?.message ?? "provision failed"}
59
66
  Next: ${result.error?.userAction ?? rerunCommand}`);
@@ -92,7 +99,6 @@ function provisionPositionals(rawArgv) {
92
99
  const valueFlags = /* @__PURE__ */ new Set([
93
100
  "--adapter",
94
101
  "--target",
95
- "--project-root",
96
102
  "--platform",
97
103
  "--device-platform",
98
104
  "--branch",