@deeeed/metamask-harness 0.79.0 → 0.81.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 (252) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +1 -1
  3. package/adapters/extension/ensure-browser.sh +2 -2
  4. package/adapters/extension/inject.mjs +0 -1
  5. package/adapters/extension/launch-browser.cjs +39 -613
  6. package/adapters/extension/readiness.mjs +2 -5
  7. package/adapters/extension/reattach.sh +2 -2
  8. package/adapters/extension/stamp-runtime-title.cjs +1 -1
  9. package/adapters/manifest.json +16 -64
  10. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
  11. package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
  12. package/adapters/mobile/inject.sh +16 -0
  13. package/adapters/mobile/launch-console-forwarder.cjs +1 -2
  14. package/adapters/mobile/open-device.sh +2 -2
  15. package/adapters/mobile/reload-app.mjs +4 -4
  16. package/adapters/mobile/reset-app-data.sh +1 -1
  17. package/adapters/mobile/start-console-forwarder.sh +3 -2
  18. package/adapters/mobile/start-metro.sh +17 -12
  19. package/adapters/mobile/wait-for-bridge.cjs +2 -2
  20. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  21. package/adapters/shared/open-debug.mjs +2 -2
  22. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  23. package/adapters/terminal/cleanup.mjs +2 -1
  24. package/adapters/terminal/inject.mjs +2 -1
  25. package/adapters/terminal/launch.mjs +17 -13
  26. package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
  27. package/adapters/terminal/lib/readiness.mjs +20 -12
  28. package/adapters/terminal/policy.mjs +35 -0
  29. package/adapters/terminal/verify.mjs +10 -3
  30. package/adapters/terminal/wallet-host.mjs +43 -225
  31. package/bin/mm-harness +9 -28
  32. package/dist/adapters/checkout-files.js +16 -0
  33. package/dist/adapters/console-capture.js +14 -1
  34. package/dist/adapters/core/actions.js +8 -4
  35. package/dist/adapters/core/overlay.js +1 -1
  36. package/dist/adapters/core/readiness.js +28 -1
  37. package/dist/adapters/core/run.js +2 -2
  38. package/dist/adapters/core/surface.js +16 -1
  39. package/dist/adapters/extension/actions.js +6 -2
  40. package/dist/adapters/extension/browser-pid.js +56 -0
  41. package/dist/adapters/extension/console-capture.js +1 -1
  42. package/dist/adapters/extension/doctor-checks.js +1 -1
  43. package/dist/adapters/extension/ensure-ready.js +5 -4
  44. package/dist/adapters/extension/extension-id.js +1 -1
  45. package/dist/adapters/extension/feature-flags.js +1 -1
  46. package/dist/adapters/extension/fixtures.js +1 -2
  47. package/dist/adapters/extension/launch.js +4 -5
  48. package/dist/adapters/extension/launched-browser.js +1 -2
  49. package/dist/adapters/extension/network-observer.js +14 -1
  50. package/dist/adapters/extension/overlay.js +1 -1
  51. package/dist/adapters/extension/product-config.js +1 -1
  52. package/dist/adapters/extension/readiness.js +1 -1
  53. package/dist/adapters/extension/reload.js +1 -1
  54. package/dist/adapters/extension/run.js +135 -14
  55. package/dist/adapters/extension/runtime-decision.js +2 -1
  56. package/dist/adapters/extension/runtime.js +9 -5
  57. package/dist/adapters/extension/surface.js +15 -2
  58. package/dist/adapters/extension/verify.js +2 -2
  59. package/dist/adapters/harness-freshness.js +2 -1
  60. package/dist/adapters/mobile/actions.js +4 -1
  61. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  62. package/dist/adapters/mobile/app-identity.js +10 -0
  63. package/dist/adapters/mobile/doctor-checks.js +1 -1
  64. package/dist/adapters/mobile/fixtures.js +1 -1
  65. package/dist/adapters/mobile/launch.js +27 -28
  66. package/dist/adapters/mobile/lifecycle.js +1 -1
  67. package/dist/adapters/mobile/metro-env.js +9 -59
  68. package/dist/adapters/mobile/network-observer.js +68 -0
  69. package/dist/adapters/mobile/performance-observer.js +2 -4
  70. package/dist/adapters/mobile/prepare-target.js +1 -1
  71. package/dist/adapters/mobile/prepare.js +3 -4
  72. package/dist/adapters/mobile/provision.js +1 -1
  73. package/dist/adapters/mobile/readiness.js +1 -1
  74. package/dist/adapters/mobile/recording-target.js +15 -0
  75. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  76. package/dist/adapters/mobile/reload.js +1 -1
  77. package/dist/adapters/mobile/run.js +39 -15
  78. package/dist/adapters/mobile/runtime-decision.js +2 -4
  79. package/dist/adapters/mobile/source-freshness.js +22 -126
  80. package/dist/adapters/mobile/surface.js +26 -3
  81. package/dist/adapters/mobile/ui.js +16 -5
  82. package/dist/adapters/mobile/verify.js +2 -3
  83. package/dist/adapters/performance/cdp-trace.js +1 -1
  84. package/dist/adapters/performance/device-platform.js +1 -1
  85. package/dist/adapters/perps-order-inputs.js +36 -0
  86. package/dist/adapters/proof-failure.js +11 -0
  87. package/dist/adapters/slot-ports.js +1 -1
  88. package/dist/adapters/surface.js +1 -0
  89. package/dist/adapters/terminal/actions.js +4 -1
  90. package/dist/adapters/terminal/console-capture.js +1 -1
  91. package/dist/adapters/terminal/doctor-checks.js +2 -0
  92. package/dist/adapters/terminal/launch.js +4 -1
  93. package/dist/adapters/terminal/overlay.js +8 -1
  94. package/dist/adapters/terminal/run.js +33 -5
  95. package/dist/adapters/terminal/surface.js +9 -1
  96. package/dist/adapters.js +27 -7
  97. package/dist/advisor-policy.js +1 -1
  98. package/dist/checkout-view.js +1 -2
  99. package/dist/cli.js +59 -21
  100. package/dist/command-contract.js +1 -26
  101. package/dist/commands/check.js +33 -10
  102. package/dist/commands/checklist.js +1 -1
  103. package/dist/commands/completion-candidates.js +6 -14
  104. package/dist/commands/config.js +2 -2
  105. package/dist/commands/core-readiness.js +12 -7
  106. package/dist/commands/debug.js +3 -3
  107. package/dist/commands/device-target.js +5 -5
  108. package/dist/commands/doctor.js +2 -14
  109. package/dist/commands/domain.js +2 -2
  110. package/dist/commands/ensure-ready.js +1 -7
  111. package/dist/commands/fixtures.js +5 -6
  112. package/dist/commands/help.js +3 -4
  113. package/dist/commands/launch/device.js +10 -17
  114. package/dist/commands/launch/extension.js +2 -8
  115. package/dist/commands/launch/mobile.js +1 -1
  116. package/dist/commands/logs.js +3 -3
  117. package/dist/commands/mobile-device-view.js +4 -2
  118. package/dist/commands/overlay.js +110 -0
  119. package/dist/commands/pin.js +1 -1
  120. package/dist/commands/pr-body.js +2 -2
  121. package/dist/commands/prepare.js +4 -9
  122. package/dist/commands/provision.js +3 -12
  123. package/dist/commands/recipe-advice.js +1 -2
  124. package/dist/commands/recipe-quality.js +2 -3
  125. package/dist/commands/resolve-extension.js +1 -7
  126. package/dist/commands/review.js +4 -4
  127. package/dist/commands/runtime-decision.js +1 -6
  128. package/dist/commands/runtime-health.js +1 -7
  129. package/dist/commands/runtime-launch.js +1 -13
  130. package/dist/commands/self-test.js +5 -8
  131. package/dist/commands/status-watch.js +1 -2
  132. package/dist/commands/status.js +2 -5
  133. package/dist/commands/task-init.js +2 -3
  134. package/dist/commands/tutorial.js +2 -2
  135. package/dist/commands/update.js +1 -1
  136. package/dist/completions-cache.js +1 -1
  137. package/dist/console-classifier.js +19 -0
  138. package/dist/doctor.js +2 -3
  139. package/dist/engine.js +57 -0
  140. package/dist/funding-execution-context.js +96 -1
  141. package/dist/host.js +32 -0
  142. package/dist/index.js +1 -6
  143. package/dist/live-tsx.js +49 -0
  144. package/dist/mm-harness-cli.js +4 -6
  145. package/dist/paths.js +34 -64
  146. package/dist/performance-observation.js +3 -5
  147. package/dist/progress.js +1 -1
  148. package/dist/recipe-assistance-context.js +6 -9
  149. package/dist/recipe-security.js +2 -59
  150. package/dist/review/knowledge.js +2 -2
  151. package/dist/runner.js +7 -4
  152. package/dist/runtime-context.js +1 -1
  153. package/dist/task-view.js +2 -3
  154. package/docs/CONTRIBUTING.md +5 -3
  155. package/docs/RECIPES.md +31 -3
  156. package/docs/cli-contract.md +2 -2
  157. package/docs/feature-guard.md +90 -0
  158. package/library/README.md +3 -1
  159. package/library/actions/core/perps/_controller.mjs +153 -134
  160. package/library/actions/core/perps/_rewards.mjs +186 -0
  161. package/library/actions/core/perps/close_positions.mjs +1 -1
  162. package/library/actions/core/perps/place_order.mjs +167 -45
  163. package/library/actions/core/perps/quote_fees.mjs +61 -0
  164. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  165. package/library/actions/extension/feature_flags/state.mjs +1 -1
  166. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  167. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  168. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  169. package/library/actions/extension/perps/perps.mjs +320 -3
  170. package/library/actions/extension/perps/place_order.mjs +3 -1
  171. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  172. package/library/actions/extension/platform/cdp.mjs +73 -11
  173. package/library/actions/harness-exports.mjs +1 -6
  174. package/library/actions/mobile/app/network-control.mjs +6 -8
  175. package/library/actions/mobile/perps/perps.mjs +216 -11
  176. package/library/actions/mobile/perps/place_order.mjs +3 -1
  177. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  178. package/library/actions/mobile/platform/bridge.mjs +8 -8
  179. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  180. package/library/actions/mobile/platform/native-session.mjs +3 -4
  181. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  182. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  183. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  184. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  185. package/library/actions/shared/console-findings.mjs +1 -1
  186. package/library/actions/shared/perps/order-policy.mjs +177 -0
  187. package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
  188. package/library/actions/terminal/app/launch.mjs +3 -2
  189. package/library/actions/terminal/platform/hud.mjs +2 -1
  190. package/library/actions/terminal/platform/page.mjs +2 -1
  191. package/library/actions/terminal/platform/runtime.mjs +2 -1
  192. package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
  193. package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
  194. package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
  195. package/library/manifests/core.action-manifest.json +87 -13
  196. package/library/manifests/extension.action-manifest.json +4 -0
  197. package/library/manifests/mobile.action-manifest.json +12 -0
  198. package/package.json +8 -9
  199. package/site/assets/help-recipes.json +1 -0
  200. package/adapters/extension/lib/slot-title.cjs +0 -292
  201. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  202. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  203. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  204. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  205. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  206. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  207. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  208. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  209. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  210. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  211. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  212. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  213. package/adapters/mobile/launch-metro.cjs +0 -77
  214. package/adapters/mobile/metro-config.cjs +0 -98
  215. package/adapters/mobile/metro-log-generation.cjs +0 -106
  216. package/adapters/terminal/lib/origin.mjs +0 -30
  217. package/adapters/terminal/lib/page-script.mjs +0 -246
  218. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  219. package/dist/adapters/mobile/frame-metrics.js +0 -118
  220. package/dist/adapters/mobile/video-recorder.js +0 -289
  221. package/dist/artifact-files.js +0 -92
  222. package/dist/checkout-lock.js +0 -149
  223. package/dist/cli-color.js +0 -98
  224. package/dist/command-journal.js +0 -308
  225. package/dist/commands/call.js +0 -685
  226. package/dist/commands/last.js +0 -60
  227. package/dist/commands/launch/index.js +0 -109
  228. package/dist/commands/list-executables.js +0 -247
  229. package/dist/commands/manifest.js +0 -488
  230. package/dist/commands/parse-args.js +0 -279
  231. package/dist/commands/reload.js +0 -41
  232. package/dist/commands/run-engine.js +0 -1340
  233. package/dist/commands/run-report.js +0 -241
  234. package/dist/commands/run.js +0 -751
  235. package/dist/commands/shared.js +0 -323
  236. package/dist/commands/stop.js +0 -60
  237. package/dist/devices.js +0 -100
  238. package/dist/discovery.js +0 -15
  239. package/dist/execution-provenance.js +0 -332
  240. package/dist/harness.js +0 -451
  241. package/dist/heal-bounds.js +0 -137
  242. package/dist/json-stream.js +0 -60
  243. package/dist/leaf-invoke.js +0 -19
  244. package/dist/library-provenance.js +0 -33
  245. package/dist/live-adapter-contract.js +0 -380
  246. package/dist/metamask-action-validation.js +0 -68
  247. package/dist/network-observation.js +0 -290
  248. package/dist/recording-target.js +0 -120
  249. package/dist/run-diagnostics.js +0 -364
  250. package/dist/run-recording.js +0 -481
  251. package/dist/runtime-proof.js +0 -90
  252. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
@@ -649,7 +649,7 @@
649
649
  },
650
650
  "account": {
651
651
  "type": "string",
652
- "description": "EVM address to read positions for; defaults to the canonical wallet fixture account."
652
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
653
653
  },
654
654
  "network": {
655
655
  "type": "string",
@@ -759,7 +759,7 @@
759
759
  },
760
760
  "account": {
761
761
  "type": "string",
762
- "description": "EVM address to read orders for; defaults to the canonical wallet fixture account."
762
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
763
763
  },
764
764
  "network": {
765
765
  "type": "string",
@@ -793,7 +793,7 @@
793
793
  "properties": {
794
794
  "account": {
795
795
  "type": "string",
796
- "description": "EVM address to read account state for; defaults to the canonical wallet fixture account."
796
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
797
797
  },
798
798
  "timeout_ms": {
799
799
  "type": "number"
@@ -818,6 +818,76 @@
818
818
  ],
819
819
  "execution_capabilities": []
820
820
  },
821
+ "metamask.perps.quote_fees": {
822
+ "description": "core Quote an order's fees through the perps controller's calculateFees (HyperLiquid, testnet by default, read-only): protocol and MetaMask fee rates and amounts (plus the winning fee source on a core that reports it), with the rewards discount the headless RewardsController answers: the rewards API's answer for the selected wallet fixture account, the calls Mobile makes (UAT by default, like Mobile dev builds; CORE_PERPS_REWARDS_API_URL selects another).",
823
+ "schema": {
824
+ "type": "object",
825
+ "properties": {
826
+ "market": {
827
+ "type": "string",
828
+ "description": "Market to quote, e.g. BTC."
829
+ },
830
+ "symbol": {
831
+ "type": "string",
832
+ "description": "Alias for market."
833
+ },
834
+ "order_type": {
835
+ "type": "string",
836
+ "enum": [
837
+ "market",
838
+ "limit"
839
+ ],
840
+ "description": "Fee schedule to quote; defaults to market."
841
+ },
842
+ "is_maker": {
843
+ "type": "boolean",
844
+ "description": "Quote maker fees (limit orders that rest)."
845
+ },
846
+ "amount": {
847
+ "type": [
848
+ "string",
849
+ "number"
850
+ ],
851
+ "description": "Order notional in USD; adds fee amounts and the blended rate for that size."
852
+ },
853
+ "rewards_discount_bips": {
854
+ "type": "integer",
855
+ "description": "Rewards discount the headless RewardsController answers with, an integer in basis points from 0 through 10000 (6500 = 65%). Replaces the rewards API's answer, to prove a discount the fixture accounts do not hold."
856
+ },
857
+ "account": {
858
+ "type": "string",
859
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
860
+ },
861
+ "account_name": {
862
+ "type": "string",
863
+ "description": "Alias for a wallet fixture account name."
864
+ },
865
+ "network": {
866
+ "type": "string",
867
+ "enum": [
868
+ "testnet",
869
+ "mainnet"
870
+ ],
871
+ "description": "Venue to quote. Defaults to testnet."
872
+ },
873
+ "timeout_ms": {
874
+ "type": "number"
875
+ }
876
+ },
877
+ "additionalProperties": false
878
+ },
879
+ "examples": [
880
+ {
881
+ "action": "metamask.perps.quote_fees",
882
+ "market": "BTC",
883
+ "amount": "100",
884
+ "rewards_discount_bips": 6500,
885
+ "intent": "Quote a $100 BTC market order with a 65% rewards discount",
886
+ "next": "done"
887
+ }
888
+ ],
889
+ "execution_capabilities": []
890
+ },
821
891
  "metamask.perps.read_snapshot": {
822
892
  "description": "Read global market and account snapshots through the public PerpsController API used by wallet clients.",
823
893
  "schema": {
@@ -835,7 +905,7 @@
835
905
  },
836
906
  "account": {
837
907
  "type": "string",
838
- "description": "Wallet fixture account name or EVM address; defaults to dev1."
908
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
839
909
  },
840
910
  "account_name": {
841
911
  "type": "string",
@@ -1023,9 +1093,13 @@
1023
1093
  "type": "number",
1024
1094
  "default": 3
1025
1095
  },
1096
+ "close_immediately": {
1097
+ "type": "boolean",
1098
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
1099
+ },
1026
1100
  "account": {
1027
1101
  "type": "string",
1028
- "description": "EVM address to trade for; defaults to the canonical wallet fixture account."
1102
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
1029
1103
  },
1030
1104
  "timeout_ms": {
1031
1105
  "type": "number"
@@ -1344,7 +1418,7 @@
1344
1418
  },
1345
1419
  "account": {
1346
1420
  "type": "string",
1347
- "description": "EVM address to close for; defaults to the canonical wallet fixture account."
1421
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
1348
1422
  },
1349
1423
  "timeout_ms": {
1350
1424
  "type": "number"
@@ -1471,7 +1545,7 @@
1471
1545
  },
1472
1546
  "account": {
1473
1547
  "type": "string",
1474
- "description": "EVM address to read for; defaults to the canonical wallet fixture account."
1548
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
1475
1549
  },
1476
1550
  "network": {
1477
1551
  "type": "string",
@@ -1627,7 +1701,7 @@
1627
1701
  },
1628
1702
  "account": {
1629
1703
  "type": "string",
1630
- "description": "EVM address to trade for; defaults to the canonical wallet fixture account."
1704
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
1631
1705
  },
1632
1706
  "timeout_ms": {
1633
1707
  "type": "number"
@@ -1681,7 +1755,7 @@
1681
1755
  },
1682
1756
  "account": {
1683
1757
  "type": "string",
1684
- "description": "Wallet fixture account name used for signing; defaults to dev1."
1758
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
1685
1759
  },
1686
1760
  "market": {
1687
1761
  "type": "string",
@@ -1803,7 +1877,7 @@
1803
1877
  "properties": {
1804
1878
  "account": {
1805
1879
  "type": "string",
1806
- "description": "Wallet fixture account name, defaulting to dev1. Without a fixture, an EVM address permits read-only assertions."
1880
+ "description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
1807
1881
  },
1808
1882
  "market": {
1809
1883
  "type": "string",
@@ -2010,7 +2084,7 @@
2010
2084
  "properties": {
2011
2085
  "account": {
2012
2086
  "type": "string",
2013
- "description": "Wallet fixture account name used for convergence and assertions; defaults to dev1."
2087
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
2014
2088
  },
2015
2089
  "market": {
2016
2090
  "type": "string",
@@ -2887,7 +2961,7 @@
2887
2961
  },
2888
2962
  "account": {
2889
2963
  "type": "string",
2890
- "description": "EVM address to act for; defaults to MM_TEST_ACCOUNT_ADDRESS."
2964
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
2891
2965
  },
2892
2966
  "timeout_ms": {
2893
2967
  "type": "number"
@@ -3010,7 +3084,7 @@
3010
3084
  },
3011
3085
  "account": {
3012
3086
  "type": "string",
3013
- "description": "EVM address to act for; defaults to MM_TEST_ACCOUNT_ADDRESS."
3087
+ "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
3014
3088
  },
3015
3089
  "timeout_ms": {
3016
3090
  "type": "number"
@@ -4371,6 +4371,10 @@
4371
4371
  "type": "number",
4372
4372
  "default": 1
4373
4373
  },
4374
+ "close_immediately": {
4375
+ "type": "boolean",
4376
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
4377
+ },
4374
4378
  "provider": {
4375
4379
  "type": "string",
4376
4380
  "description": "Exact Perps provider required before background dispatch."
@@ -2775,6 +2775,18 @@
2775
2775
  "leverage": {
2776
2776
  "type": "number",
2777
2777
  "default": 3
2778
+ },
2779
+ "network": {
2780
+ "type": "string",
2781
+ "enum": [
2782
+ "mainnet",
2783
+ "testnet"
2784
+ ],
2785
+ "description": "Perps network the order must run on; checked against the live app. A mainnet order must declare mainnet."
2786
+ },
2787
+ "close_immediately": {
2788
+ "type": "boolean",
2789
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
2778
2790
  }
2779
2791
  },
2780
2792
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.79.0",
3
+ "version": "0.81.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -21,18 +21,18 @@
21
21
  "check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
22
22
  },
23
23
  "dependencies": {
24
- "@farmslot/adapter-sdk": "0.1.0",
25
- "@farmslot/adapter-web": "0.1.0",
26
- "@farmslot/agent-runtime": "0.16.0",
27
- "@farmslot/expo-recipe": "0.14.0",
24
+ "@farmslot/adapter-rn": "0.17.0",
25
+ "@farmslot/adapter-sdk": "0.4.0",
26
+ "@farmslot/adapter-web": "0.3.1",
27
+ "@farmslot/agent-runtime": "0.17.0",
28
28
  "@farmslot/handoff": "^0.3.1",
29
29
  "@farmslot/protocol": "0.34.0",
30
- "@farmslot/recipe-cli": "0.2.0",
31
- "@farmslot/recipe-runner": "0.23.0",
30
+ "@farmslot/recipe-cli": "0.5.0",
31
+ "@farmslot/recipe-runner": "0.24.0",
32
32
  "agent-device": "0.19.3",
33
33
  "commander": "^12.0.0",
34
- "es-module-lexer": "2.3.1",
35
34
  "esbuild": "0.28.1",
35
+ "tsx": "^4.23.0",
36
36
  "viem": "^2.54.3",
37
37
  "ws": "8.21.0"
38
38
  },
@@ -48,7 +48,6 @@
48
48
  "eslint": "^9",
49
49
  "globals": "^17.7.0",
50
50
  "proper-lockfile": "^4.1.2",
51
- "tsx": "^4.23.0",
52
51
  "typescript": "^5.6.0",
53
52
  "typescript-eslint": "^8",
54
53
  "vitest": "^4.1.10"
@@ -173,6 +173,7 @@
173
173
  "Graph: no unconditional pass, generic intent, hidden start state, or opaque node that collapses preparation, action, assertion, evidence, and teardown.",
174
174
  "Assertion modes such as require_unchanged must fail before any corrective action. Verify the negative path leaves the inspected state untouched; an assertion must not repair the behavior it is supposed to test.",
175
175
  "Reuse structured reads with assert_output instead of adding a new domain action for every comparison: source names the earlier node, and assert can select $.matchingCount with operator eq and the expected parameter. A status-report action can succeed while reporting missing prerequisites; assert required values before the dependent action. Checking three rows exist does not prove there are exactly three, or that they belong to this task.",
176
+ "Test command nodes: assert_output on the literal pass count. An exit code alone is not proof: jest -t exits 0 when no test matches.",
176
177
  "Evidence: inspect the recipe resolution, summary, trace, manifest, logs, and actual artifacts. Verify the values and state required by the claim.",
177
178
  "For a change claim, compare the actual before/after values for the same subject. Two captures alone prove neither an update nor freshness. Preserve unchanged observations as unproven rather than retrying to obtain a pass.",
178
179
  {
@@ -1,292 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Operator slot title for headed extension Chrome.
5
- *
6
- * Chrome's window label is the active tab's document.title. Farmslot stamps
7
- * home.html to "<slot-id> — MetaMask" so operators can tell which slot owns a
8
- * window when several farms run side by side.
9
- *
10
- * Stamping is best-effort: missing slot context or an uninspectable tab must
11
- * never fail launch/readiness. A MutationObserver keeps the prefix across
12
- * MetaMask SPA title resets.
13
- */
14
-
15
- const fs = require('node:fs');
16
- const http = require('node:http');
17
- const path = require('node:path');
18
-
19
- function sanitizeSlotId(value) {
20
- const slotId = typeof value === 'string' ? value.trim() : '';
21
- return /^[A-Za-z0-9._:-]{1,64}$/u.test(slotId) ? slotId : '';
22
- }
23
-
24
- function readSlotId(target, runtimeDir) {
25
- for (const key of ['RECIPE_SLOT_ID', 'SLOT_ID', 'FARMSLOT_SLOT_ID']) {
26
- const value = sanitizeSlotId(process.env[key]);
27
- if (value) return value;
28
- }
29
- if (!target || !runtimeDir) return '';
30
- try {
31
- const runtimeContext = JSON.parse(
32
- fs.readFileSync(path.join(target, runtimeDir, 'agentic-runtime.json'), 'utf8'),
33
- );
34
- return sanitizeSlotId(runtimeContext.slotId);
35
- } catch {
36
- // Standalone harness users do not need a custom browser title.
37
- return '';
38
- }
39
- }
40
-
41
- /**
42
- * Serialized page-evaluation callback. Must stay a plain function (no closure).
43
- */
44
- function applyPersistentSlotTitle(slot) {
45
- // Inline sanitize because the serialized function cannot call Node helpers.
46
- const id =
47
- typeof slot === 'string' && /^[A-Za-z0-9._:-]{1,64}$/u.test(slot.trim())
48
- ? slot.trim()
49
- : '';
50
- if (!id) return document.title;
51
- window.__farmslotSlotId = id;
52
-
53
- const stripSlotPrefixes = (title) => {
54
- let base = String(title || 'MetaMask');
55
- // Accept em dash, en dash, or hyphen — historical stampers mixed them.
56
- for (let i = 0; i < 32; i += 1) {
57
- const stripped = base.replace(/^.+?\s+[—–-]\s+/u, '');
58
- if (stripped === base) break;
59
- base = stripped;
60
- }
61
- return base || 'MetaMask';
62
- };
63
-
64
- const desiredTitle = () => {
65
- const id = window.__farmslotSlotId;
66
- if (!id) return document.title;
67
- return `${id} — ${stripSlotPrefixes(document.title)}`;
68
- };
69
-
70
- const setTitle = () => {
71
- if (window.__farmslotSlotTitleLock) {
72
- window.__farmslotSlotTitlePending = true;
73
- return;
74
- }
75
- const next = desiredTitle();
76
- if (document.title === next) return;
77
- window.__farmslotSlotTitleLock = true;
78
- try {
79
- document.title = next;
80
- } finally {
81
- // Release after the title mutation is delivered so the observer cannot
82
- // re-enter and stack prefixes while React also mutates the DOM. If a
83
- // mutation arrived while locked, re-apply once after unlock.
84
- queueMicrotask(() => {
85
- window.__farmslotSlotTitleLock = false;
86
- if (window.__farmslotSlotTitlePending) {
87
- window.__farmslotSlotTitlePending = false;
88
- setTitle();
89
- }
90
- });
91
- }
92
- };
93
-
94
- setTitle();
95
- if (!window.__farmslotSlotTitleObserver) {
96
- let node = document.querySelector('title');
97
- if (!node) {
98
- node = document.createElement('title');
99
- (document.head || document.documentElement).appendChild(node);
100
- }
101
- window.__farmslotSlotTitleObserver = new MutationObserver(setTitle);
102
- // Observe only the <title> element — watching head/documentElement with
103
- // subtree:true re-fires on every React DOM mutation and can livelock CDP.
104
- window.__farmslotSlotTitleObserver.observe(node, {
105
- childList: true,
106
- characterData: true,
107
- subtree: true,
108
- });
109
- }
110
- return document.title;
111
- }
112
-
113
- function buildStampExpression(slotId) {
114
- const slot = sanitizeSlotId(slotId);
115
- if (!slot) return 'document.title';
116
- // Single source of truth: serialize the Playwright callback for CDP evaluate.
117
- return `(${applyPersistentSlotTitle.toString()})(${JSON.stringify(slot)})`;
118
- }
119
-
120
- function assertLocalUrl(url) {
121
- if (!/^http:\/\/127\.0\.0\.1:\d+\//u.test(url)) {
122
- throw new Error(`slot-title: refusing non-local URL: ${url}`);
123
- }
124
- }
125
-
126
- function assertLocalWebSocketUrl(url) {
127
- if (!/^ws:\/\/(?:127\.0\.0\.1|localhost):\d+\//u.test(String(url))) {
128
- throw new Error(`slot-title: refusing non-local websocket URL: ${url}`);
129
- }
130
- }
131
-
132
- function httpJson(url, timeoutMs = 3000) {
133
- assertLocalUrl(url);
134
- return new Promise((resolve, reject) => {
135
- const req = http.get(url, (res) => {
136
- let data = '';
137
- res.setEncoding('utf8');
138
- res.on('data', (chunk) => {
139
- data += chunk;
140
- });
141
- res.on('end', () => {
142
- try {
143
- resolve(JSON.parse(data));
144
- } catch (err) {
145
- reject(new Error(`invalid JSON from ${url}: ${err.message}`));
146
- }
147
- });
148
- });
149
- req.setTimeout(timeoutMs, () => {
150
- req.destroy(new Error(`timeout from ${url}`));
151
- });
152
- req.on('error', reject);
153
- });
154
- }
155
-
156
- function resolveWebSocket(target) {
157
- try {
158
- // Prefer the target checkout's ws, then the harness install.
159
- const { createRequire } = require('node:module');
160
- const req = createRequire(__filename);
161
- if (target) {
162
- try {
163
- return req(req.resolve('ws', { paths: [target] }));
164
- } catch {
165
- // Fall through to harness / global.
166
- }
167
- }
168
- return req('ws');
169
- } catch {
170
- return typeof WebSocket === 'function' ? WebSocket : null;
171
- }
172
- }
173
-
174
- async function cdpEvaluate(target, webSocketDebuggerUrl, expression, timeoutMs = 5000) {
175
- assertLocalWebSocketUrl(webSocketDebuggerUrl);
176
- const WebSocketImpl = resolveWebSocket(target);
177
- if (!WebSocketImpl) return { skipped: true, reason: 'WebSocket unavailable' };
178
- return new Promise((resolve, reject) => {
179
- const ws = new WebSocketImpl(webSocketDebuggerUrl);
180
- const timer = setTimeout(() => {
181
- try {
182
- ws.close();
183
- } catch {
184
- // Best-effort timeout cleanup.
185
- }
186
- reject(new Error('timeout evaluating extension page via CDP'));
187
- }, timeoutMs);
188
- const onOpen = () => {
189
- ws.send(
190
- JSON.stringify({
191
- id: 1,
192
- method: 'Runtime.evaluate',
193
- params: { expression, awaitPromise: true, returnByValue: true },
194
- }),
195
- );
196
- };
197
- const onMessage = (event) => {
198
- const raw = event?.data ?? event;
199
- const msg = JSON.parse(Buffer.isBuffer(raw) ? raw.toString('utf8') : String(raw));
200
- if (msg.id !== 1) return;
201
- clearTimeout(timer);
202
- ws.close();
203
- if (msg.error) {
204
- reject(new Error(msg.error.message || JSON.stringify(msg.error)));
205
- return;
206
- }
207
- resolve(msg.result?.result?.value ?? null);
208
- };
209
- const onError = (err) => {
210
- clearTimeout(timer);
211
- reject(new Error(`CDP websocket error while stamping slot title: ${err?.message || err || 'unknown'}`));
212
- };
213
- if (typeof ws.on === 'function') {
214
- ws.on('open', onOpen);
215
- ws.on('message', onMessage);
216
- ws.on('error', onError);
217
- } else {
218
- ws.addEventListener('open', onOpen);
219
- ws.addEventListener('message', onMessage);
220
- ws.addEventListener('error', onError);
221
- }
222
- });
223
- }
224
-
225
- function isHomePage(target, extensionId) {
226
- if (!target || target.type !== 'page' || typeof target.url !== 'string') return false;
227
- if (!target.url.startsWith(`chrome-extension://${extensionId}/`)) return false;
228
- return target.url.includes('/home.html');
229
- }
230
-
231
- /**
232
- * Stamp every inspectable home.html tab for this extension over CDP.
233
- * Attached tabs (no webSocketDebuggerUrl) are skipped — another client owns them.
234
- */
235
- async function stampHomeTabsViaCdp(options) {
236
- const {
237
- target = process.cwd(),
238
- cdpPort,
239
- extensionId,
240
- slotId: slotIdOption,
241
- runtimeDir,
242
- } = options || {};
243
- const slotId = sanitizeSlotId(slotIdOption) || readSlotId(target, runtimeDir);
244
- const empty = { slotId: slotId || '', stamped: 0, skipped: 0, titles: [] };
245
- if (!slotId || !cdpPort || !extensionId) return empty;
246
-
247
- let targets;
248
- try {
249
- targets = await httpJson(`http://127.0.0.1:${cdpPort}/json/list`);
250
- } catch {
251
- return empty;
252
- }
253
- if (!Array.isArray(targets)) return empty;
254
-
255
- const homes = targets.filter((t) => isHomePage(t, extensionId));
256
- const titles = [];
257
- let stamped = 0;
258
- let skipped = 0;
259
- const expression = buildStampExpression(slotId);
260
-
261
- for (const home of homes) {
262
- if (typeof home.webSocketDebuggerUrl !== 'string') {
263
- skipped += 1;
264
- continue;
265
- }
266
- try {
267
- const title = await cdpEvaluate(target, home.webSocketDebuggerUrl, expression);
268
- if (title && typeof title === 'object' && title.skipped) {
269
- skipped += 1;
270
- continue;
271
- }
272
- if (typeof title === 'string') {
273
- titles.push(title);
274
- stamped += 1;
275
- } else {
276
- skipped += 1;
277
- }
278
- } catch {
279
- skipped += 1;
280
- }
281
- }
282
-
283
- return { slotId, stamped, skipped, titles };
284
- }
285
-
286
- module.exports = {
287
- sanitizeSlotId,
288
- readSlotId,
289
- applyPersistentSlotTitle,
290
- buildStampExpression,
291
- stampHomeTabsViaCdp,
292
- };