@deeeed/metamask-harness 0.41.1 → 0.43.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 (52) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +12 -0
  3. package/adapters/manifest.json +8 -0
  4. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +108 -10
  5. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +117 -16
  6. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +2 -0
  7. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +921 -0
  8. package/adapters/mobile/bridge-runtime/lib/config.cjs +14 -4
  9. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +177 -0
  10. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +13 -3
  11. package/adapters/mobile/reload-app.mjs +67 -0
  12. package/adapters/mobile/start-console-forwarder.sh +22 -3
  13. package/adapters/mobile/start-metro.sh +6 -11
  14. package/adapters/mobile/stop-metro.sh +10 -1
  15. package/adapters/shared/open-debug.mjs +172 -2
  16. package/dist/adapters/extension/browser-cdp.js +174 -0
  17. package/dist/adapters/extension/network-observer.js +209 -0
  18. package/dist/adapters/extension/performance-observer.js +75 -0
  19. package/dist/adapters/mobile/frame-metrics.js +45 -0
  20. package/dist/adapters/mobile/metro-env.js +0 -5
  21. package/dist/adapters/mobile/performance-observer.js +43 -0
  22. package/dist/adapters/mobile/prepare.js +1 -3
  23. package/dist/adapters/mobile/runtime-decision.js +6 -9
  24. package/dist/adapters/performance/cdp-trace.js +342 -0
  25. package/dist/adapters/performance/js-task-metrics.js +35 -0
  26. package/dist/adapters.js +29 -1
  27. package/dist/artifact-files.js +92 -0
  28. package/dist/async.js +19 -0
  29. package/dist/cli-commands.js +6 -3
  30. package/dist/cli.js +4 -0
  31. package/dist/command-contract.js +3 -0
  32. package/dist/commands/call.js +66 -20
  33. package/dist/commands/reload.js +80 -0
  34. package/dist/commands/run-engine.js +18 -0
  35. package/dist/commands/run.js +68 -22
  36. package/dist/mm-harness-cli.js +17 -1
  37. package/dist/network-observation.js +283 -0
  38. package/dist/performance-observation.js +465 -0
  39. package/docs/NETWORK-CAPTURE.md +98 -0
  40. package/docs/PERFORMANCE-CAPTURE.md +33 -0
  41. package/docs/RECIPES.md +17 -0
  42. package/library/actions/mobile/app/network_assert.mjs +14 -0
  43. package/library/actions/mobile/app/network_capture.mjs +72 -0
  44. package/library/actions/mobile/platform/bridge.mjs +10 -2
  45. package/library/actions/shared/app/network-artifact.mjs +10 -0
  46. package/library/actions/shared/app/network-assert.mjs +154 -0
  47. package/library/manifests/extension.action-manifest.json +173 -0
  48. package/library/manifests/mobile.action-manifest.json +204 -0
  49. package/library/recipes/mobile/perps/performance.recipe.json +11 -11
  50. package/package.json +1 -1
  51. package/scripts/completions.sh +2 -1
  52. package/scripts/site-contrast.mjs +43 -27
@@ -168,25 +168,25 @@
168
168
  "value": "{{params.content_variant}}",
169
169
  "equals": "trending",
170
170
  "cases": {
171
- "match": "assert-no-positions"
171
+ "match": "ensure-no-positions"
172
172
  },
173
173
  "default": "assert-position",
174
- "intent": "Prove either the existing position or the empty account before timing"
174
+ "intent": "Prepare either the existing position or an empty account before timing"
175
175
  },
176
- "assert-no-positions": {
177
- "action": "metamask.perps.assert_positions",
176
+ "ensure-no-positions": {
177
+ "action": "metamask.perps.ensure_positions",
178
178
  "state": "none",
179
179
  "mode": "all",
180
- "timeout_ms": 30000,
181
- "intent": "Prove the market-only account has no open positions",
182
- "next": "assert-no-orders"
180
+ "timeout_ms": 120000,
181
+ "intent": "Close all positions outside timing so the market-only account is empty",
182
+ "next": "ensure-no-orders"
183
183
  },
184
- "assert-no-orders": {
185
- "action": "metamask.perps.assert_orders",
184
+ "ensure-no-orders": {
185
+ "action": "metamask.perps.ensure_orders",
186
186
  "state": "none",
187
187
  "mode": "all",
188
- "timeout_ms": 30000,
189
- "intent": "Prove the market-only account has no open orders",
188
+ "timeout_ms": 120000,
189
+ "intent": "Cancel all orders outside timing so the market-only account is empty",
190
190
  "next": "select-account-switch-preparation"
191
191
  },
192
192
  "assert-position": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.41.1",
3
+ "version": "0.43.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -29,7 +29,7 @@ _mmh_bin() {
29
29
  return 1
30
30
  }
31
31
 
32
- _mmh_commands="launch stop logs debug fixtures actions call run doctor check checklist execution-template recipe-quality install verify cleanup completions"
32
+ _mmh_commands="launch stop logs debug reload fixtures actions call run doctor check checklist execution-template recipe-quality install verify cleanup completions"
33
33
 
34
34
  # Per-command flags (static, from the mm-harness surface).
35
35
  _mmh_flags_for() {
@@ -38,6 +38,7 @@ _mmh_flags_for() {
38
38
  stop) printf '%s' "--port --target --json" ;;
39
39
  logs) printf '%s' "--full --events --source --adapter --target --json" ;;
40
40
  debug) printf '%s' "--worker --dev-menu --adapter --target --json" ;;
41
+ reload) printf '%s' "--adapter --platform --target --json" ;;
41
42
  fixtures) printf '%s' "--from --dev --force --fixture --adapter --target --json" ;;
42
43
  checklist) printf '%s' "" ;;
43
44
  execution-template) printf '%s' "--dir --domain-dir --project-worker --project-name --package-templates --package-id --flow --run-mode --platform --domain --id --provenance --include-shadowed --no-include-shadowed --title --force --json" ;;
@@ -272,6 +272,18 @@ function findChrome() {
272
272
  }
273
273
 
274
274
  async function startChrome() {
275
+ let lastError;
276
+ for (let attempt = 0; attempt < 2; attempt += 1) {
277
+ try {
278
+ return await startChromeOnce();
279
+ } catch (error) {
280
+ lastError = error;
281
+ }
282
+ }
283
+ throw lastError;
284
+ }
285
+
286
+ async function startChromeOnce() {
275
287
  const binary = findChrome();
276
288
  const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'site-contrast-'));
277
289
  const child = spawn(
@@ -279,6 +291,7 @@ async function startChrome() {
279
291
  [
280
292
  '--headless=new',
281
293
  '--disable-gpu',
294
+ '--disable-dev-shm-usage',
282
295
  '--no-first-run',
283
296
  '--no-default-browser-check',
284
297
  '--remote-debugging-port=0',
@@ -290,35 +303,38 @@ async function startChrome() {
290
303
 
291
304
  // Chrome writes the chosen port to DevToolsActivePort once it is listening.
292
305
  const portFile = path.join(userDataDir, 'DevToolsActivePort');
293
- const port = await waitFor(() => {
294
- if (!fs.existsSync(portFile)) return null;
295
- const line = fs.readFileSync(portFile, 'utf8').split('\n')[0].trim();
296
- return line ? Number(line) : null;
297
- }, 15000, 'Chrome did not report a debugging port');
298
-
299
- const version = await getJson(`http://127.0.0.1:${port}/json/version`);
300
- return {
301
- endpoint: version.webSocketDebuggerUrl,
302
- port,
303
- close: async () => {
304
- child.kill();
305
- // Chrome holds the profile dir open until it exits; removing it early
306
- // fails with ENOTEMPTY. Wait for exit, and never fail the gate on cleanup.
307
- await new Promise((done) => {
308
- if (child.exitCode !== null || child.signalCode !== null) return done();
309
- const timer = setTimeout(done, 5000);
310
- child.once('exit', () => {
311
- clearTimeout(timer);
312
- done();
313
- });
306
+ const close = async () => {
307
+ child.kill();
308
+ // Chrome holds the profile dir open until it exits; removing it early
309
+ // fails with ENOTEMPTY. Wait for exit, and never fail the gate on cleanup.
310
+ await new Promise((done) => {
311
+ if (child.exitCode !== null || child.signalCode !== null) return done();
312
+ const timer = setTimeout(done, 5000);
313
+ child.once('exit', () => {
314
+ clearTimeout(timer);
315
+ done();
314
316
  });
315
- try {
316
- fs.rmSync(userDataDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
317
- } catch {
318
- /* a leftover temp profile is not worth failing a contrast run over */
319
- }
320
- },
317
+ });
318
+ try {
319
+ fs.rmSync(userDataDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
320
+ } catch {
321
+ /* a leftover temp profile is not worth failing a contrast run over */
322
+ }
321
323
  };
324
+
325
+ try {
326
+ const port = await waitFor(() => {
327
+ if (!fs.existsSync(portFile)) return null;
328
+ const line = fs.readFileSync(portFile, 'utf8').split('\n')[0].trim();
329
+ return line ? Number(line) : null;
330
+ }, 15000, 'Chrome did not report a debugging port');
331
+
332
+ const version = await getJson(`http://127.0.0.1:${port}/json/version`);
333
+ return { endpoint: version.webSocketDebuggerUrl, port, close };
334
+ } catch (error) {
335
+ await close();
336
+ throw error;
337
+ }
322
338
  }
323
339
 
324
340
  async function auditPage(browserEndpoint, url) {