@deeeed/metamask-harness 0.38.1 → 0.40.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.
- package/CHANGELOG.md +58 -0
- package/adapters/extension/lib/playwright-cdp.cjs +17 -1
- package/adapters/extension/sidepanel-toggle.sh +135 -13
- package/dist/adapters/mobile/release-artifact.js +65 -0
- package/dist/adapters.js +27 -17
- package/dist/command-contract.js +10 -2
- package/dist/commands/call.js +7 -4
- package/dist/commands/launch/index.js +55 -3
- package/dist/commands/parse-args.js +6 -0
- package/dist/commands/run-engine.js +75 -39
- package/dist/funding-execution-context.js +1386 -0
- package/dist/live-adapter-contract.js +3 -1
- package/dist/metamask-action-validation.js +170 -3
- package/dist/mm-harness-cli.js +8 -2
- package/dist/recipe-security.js +5 -12
- package/dist/runner.js +102 -6
- package/docs/RECIPES.md +74 -1
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +6 -5
- package/library/actions/extension/assets/open_details.mjs +30 -0
- package/library/actions/extension/assets/set_token_visibility.mjs +5 -0
- package/library/actions/extension/networks/add_chainlist.mjs +4 -0
- package/library/actions/extension/networks/add_custom.mjs +4 -0
- package/library/actions/extension/networks/custom_network.mjs +48 -1
- package/library/actions/extension/perps/assert_visible_consistency.mjs +366 -0
- package/library/actions/extension/perps/close_visible_position.mjs +299 -37
- package/library/actions/extension/perps/compare_provider_market.mjs +77 -0
- package/library/actions/extension/perps/edit_margin.mjs +285 -44
- package/library/actions/extension/perps/mutation-receipt.mjs +781 -0
- package/library/actions/extension/perps/perps.mjs +1029 -363
- package/library/actions/extension/perps/read_funds_confirmation.mjs +126 -13
- package/library/actions/extension/perps/read_snapshot.mjs +124 -3
- package/library/actions/extension/perps/read_visible_state.mjs +63 -5
- package/library/actions/extension/perps/search_markets.mjs +32 -0
- package/library/actions/extension/perps/select_activity_filter.mjs +81 -18
- package/library/actions/extension/perps/select_market_filter.mjs +48 -4
- package/library/actions/extension/perps/set_market_favorite.mjs +6 -1
- package/library/actions/extension/perps/update_position_tpsl.mjs +251 -69
- package/library/actions/extension/perps/visible-mutation-identity.mjs +164 -0
- package/library/actions/extension/platform/cdp.mjs +88 -9
- package/library/actions/extension/settings/set_basic_functionality.mjs +35 -7
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +2 -6
- package/library/actions/extension/swap_bridge/set_amount.mjs +11 -2
- package/library/actions/extension/ui/navigate.mjs +45 -5
- package/library/actions/extension/wallet/import.mjs +70 -29
- package/library/actions/extension/wallet/lock.mjs +62 -5
- package/library/actions/extension/wallet/visible-session.mjs +218 -0
- package/library/actions/mobile/assets/import_custom_token.mjs +181 -0
- package/library/actions/mobile/assets/open_details.mjs +43 -0
- package/library/actions/mobile/assets/read_details.mjs +96 -0
- package/library/actions/mobile/assets/set_token_visibility.mjs +302 -0
- package/library/actions/mobile/assets/verify_sorting.mjs +104 -0
- package/library/actions/mobile/networks/add_custom.mjs +125 -0
- package/library/actions/mobile/networks/network-management.mjs +132 -0
- package/library/actions/mobile/networks/read_visible_state.mjs +43 -0
- package/library/actions/mobile/networks/remove_custom.mjs +100 -0
- package/library/actions/mobile/perps/search_markets.mjs +46 -0
- package/library/actions/mobile/perps/set_market_favorite.mjs +117 -0
- package/library/actions/mobile/platform/native-session-name.mjs +14 -0
- package/library/actions/mobile/platform/native-session.mjs +59 -0
- package/library/actions/mobile/platform/observe-ui.mjs +9 -1
- package/library/actions/mobile/swap_bridge/native-session.mjs +10 -45
- package/library/actions/mobile/swap_bridge/select_assets.mjs +15 -1
- package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -1
- package/library/actions/mobile/swap_bridge/set_max_amount.mjs +55 -0
- package/library/actions/mobile/swap_bridge/set_slippage.mjs +96 -0
- package/library/actions/mobile/swap_bridge/submit_transaction.mjs +141 -0
- package/library/actions/mobile/ui/native-navigation.mjs +120 -23
- package/library/actions/mobile/ui/navigate.mjs +13 -2
- package/library/actions/mobile/wallet/import.mjs +17 -10
- package/library/actions/mobile/wallet/lock.mjs +14 -0
- package/library/actions/mobile/wallet/native-ui.mjs +88 -15
- package/library/actions/shared/perps/visible-state.mjs +33 -11
- package/library/actions/shared/swap-bridge/visible-state.mjs +15 -0
- package/library/manifests/extension.action-manifest.json +1707 -1241
- package/library/manifests/mobile.action-manifest.json +308 -1
- package/library/recipes/assets/release-token-details.recipe.json +5 -4
- package/library/recipes/assets/release-token-hide-readd.recipe.json +2 -0
- package/library/recipes/{assets → extension/assets}/release-custom-network-token-persistence.recipe.json +1 -0
- package/library/recipes/{perps → extension/perps}/release-activity-filters.recipe.json +5 -5
- package/library/recipes/{perps → extension/perps}/release-funds-flows.recipe.json +3 -3
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +24 -19
- package/library/recipes/extension/perps/release-market-filters.recipe.json +97 -0
- package/library/recipes/extension/perps/release-order-entry.recipe.json +4 -1
- package/library/recipes/extension/perps/release-order-validation.recipe.json +53 -14
- package/library/recipes/extension/perps/release-visible-consistency.recipe.json +47 -0
- package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +76 -0
- package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +90 -0
- package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +58 -0
- package/library/recipes/perps/release-live-market-order.recipe.json +30 -13
- package/library/recipes/perps/release-market-details.recipe.json +1 -1
- package/library/recipes/perps/release-market-search.recipe.json +8 -4
- package/library/recipes/perps/release-watchlist.recipe.json +31 -19
- package/library/recipes/swap-bridge/release-custom-slippage.recipe.json +5 -13
- package/library/recipes/swap-bridge/release-quote.recipe.json +1 -0
- package/library/recipes/wallet/import.recipe.json +4 -14
- package/package.json +1 -1
- package/library/actions/extension/assets/finish_send.mjs +0 -128
- package/library/actions/extension/swap_bridge/submit_transaction.mjs +0 -363
- package/library/actions/extension/ui/activate_and_observe.mjs +0 -96
- package/library/recipes/assets/release-custom-gas-send.recipe.json +0 -88
- package/library/recipes/assets/release-native-max-cancel.recipe.json +0 -70
- package/library/recipes/assets/release-native-max-send.recipe.json +0 -78
- package/library/recipes/assets/release-own-account-send.recipe.json +0 -80
- package/library/recipes/extension/runner/action-validation.recipe.json +0 -343
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.40.0 - 2026-08-16
|
|
6
|
+
|
|
7
|
+
- Remove Extension's public `metamask.perps.close_positions` and `metamask.perps.start_state` surfaces and its mutating `runner.action-validation` recipe; retain only state assertions it can prove without implicit readiness or unreceipted convergence.
|
|
8
|
+
- Make guarded Extension custom-network setup wait for an unobscured account-menu hit point before opening network management.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add explicit Extension fullscreen/sidepanel launch selection and auto/show/hide recipe HUD policy.
|
|
13
|
+
- Bind external-mutation runs to independently validated release, onboarding, balance-evidence, request, and preflight bytes plus the same-run visible wallet/runtime identity.
|
|
14
|
+
- Add a packaged Perps market-filter release recipe with a deterministic market-list start state and every nonconditional category control.
|
|
15
|
+
- Add official-compatible visible Extension Perps market-list/activity consistency assertions, plus source-dev-only background snapshot sampling and activity-to-history identity expectations.
|
|
16
|
+
- Bind every live Extension action result to the selected CDP target and current browser runtime session.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Canonicalize durable Extension Perps release journeys under one discovered recipe ID each, with activity and funds flows scoped to Extension and explicit official/source-development and live-mutation boundaries.
|
|
21
|
+
- Parameterize live values in the Extension Perps order-validation recipe and narrow existing market-details, activity-filter, and funds-preview claims to their asserted evidence.
|
|
22
|
+
- Require an explicit flat market for non-submitting Extension order-form recipes and make live-price movement an opt-in source-development snapshot assertion.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Trust only the current Perps-only funding registry plus the named frozen G005 digest; reject duplicate or aggregate-over-cap mutation operations and budgets, mutation-capable balance graphs, root-ref dependency shadowing, and non-visible or non-Home balance observations.
|
|
27
|
+
- Bind trusted funding and onboarding evidence to exact emitted call graphs, successful node/action order, and reachable root PASS terminals while preserving Mobile's adapter-owned external-mutation consent boundary.
|
|
28
|
+
- Stop advertising shared Perps clean-market convergence on Extension; its state helpers now expose only navigation and assertion inputs and reject ignored creation or cleanup parameters during planning.
|
|
29
|
+
- Remove unsupported Extension send completion, Swap/Bridge submission, and generic activate-and-observe capabilities plus four non-executable Assets submit recipes; retain read-only, prepare, review, quote, and Mobile-owned live Swap planning.
|
|
30
|
+
- Bind funded Extension Perps margin and TP/SL updates to the exact visible position size, enforce order caps against requested leveraged exposure before receipt publication, and default funded placements to one-times leverage.
|
|
31
|
+
- Fail closed on visible and background Perps financial mutations without an identity-bound durable receipt, current visible funding minimum, runner-owned artifact/runtime, and exact dispatch-boundary provider/network; terminally record known non-dispatch instead of retrying the same claim.
|
|
32
|
+
- Require approved visible wallet method and credential provenance plus a hash-bound emitted recipe, pass summary, artifact manifest, and exact reachable root-terminal PASS in funding evidence.
|
|
33
|
+
- Require every funded visible position edit or close to recheck current funding, provider/network, and exact target state after receipt publication and immediately before its first pointer dispatch.
|
|
34
|
+
- Invalidate funded-mutation wallet identity proof after account selection, reset, lock, unlock, setup, or any state-changing generic UI action until a complete fresh visible wallet import proof succeeds.
|
|
35
|
+
- Keep Extension Perps state wrappers observation-only so they cannot place, close, or cancel implicitly outside a leaf action's funding authorization.
|
|
36
|
+
- Preserve a final Perps navigation observation poll and require Side Panel convergence to one MetaMask product surface beside the explicitly requested dapp.
|
|
37
|
+
- Require the canonical non-submitting Swap quote recipe to prove a positive reviewable quote instead of accepting a no-route state.
|
|
38
|
+
- Refuse cleanup-bound custom-network setup when the target network already exists, preserving pre-existing wallet configuration.
|
|
39
|
+
- Bind Android release-artifact action results to the verified native app process session and redact Mobile wallet-import identity proofs to a matched digest.
|
|
40
|
+
- Distinguish obscured Perps controls from harmless edge intersections in visible layout proofs and include confirmation controls on funds surfaces.
|
|
41
|
+
- Reject duplicate rendered market, watchlist, or activity identities during optional Extension consistency proofs.
|
|
42
|
+
- Read Perps funds confirmation titles from their visible wallet-initiated header and support exact amount, token, network, percentage-control, and disabled-state assertions.
|
|
43
|
+
- Wait for Basic Functionality consent to enable its confirmation before submitting the visible settings change.
|
|
44
|
+
- Reach the Extension global wallet menu through the visible Home control before locking from headerless product routes.
|
|
45
|
+
- Keep Extension surface transitions singular and route the sidepanel launcher's obscuration hit test through hardened raw CDP when its visible notification badge overlaps the button.
|
|
46
|
+
- Namespace funded Perps claims by exact request and preflight bytes, persist identity-bound receipts before live pointer dispatch, and restrict over-cap order recovery to exact cleanup-only cancellation without upgrading placement success.
|
|
47
|
+
- Relaunch intact Extension release artifacts without source dependency repair, and support exact receipt-bound close-only Perps cleanup with primary-card and balance reconciliation proof.
|
|
48
|
+
- Validate Extension Perps activity filters against their singular route query while requiring the requested filter label to remain visible.
|
|
49
|
+
- Make Extension `wallet.import method=ui` idempotent on an onboarded profile by visibly unlocking and selecting the exact derived account without fixture or background-state probes.
|
|
50
|
+
|
|
51
|
+
## 0.39.0 - 2026-08-14
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Add opaque Android release actions for token details, sorting, visibility, custom-token import, custom-network lifecycle, Perps search and watchlists, Swap Max, slippage, guarded submission, and wallet locking through native accessibility.
|
|
56
|
+
- Make shared Assets, Perps, and Swap release recipes executable on untouched Extension and Android release artifacts with parameterized visible-state assertions.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Keep physical-device actions on isolated semantic sessions, bypass biometric authentication with password unlock, verify every recovery-phrase field, and reject incorrect token metadata as a product failure.
|
|
61
|
+
- Prove Extension token visibility changes and parse visible custom-slippage labels consistently across both clients.
|
|
62
|
+
|
|
5
63
|
## 0.38.1 - 2026-08-14
|
|
6
64
|
|
|
7
65
|
### Fixed
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { createRequire } = require('node:module');
|
|
5
|
+
|
|
6
|
+
function loadChromium() {
|
|
7
|
+
const runtimeRequire = createRequire(path.join(process.cwd(), 'package.json'));
|
|
8
|
+
try {
|
|
9
|
+
return runtimeRequire('@playwright/test').chromium;
|
|
10
|
+
} catch {
|
|
11
|
+
return runtimeRequire('playwright').chromium;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function connectBrowserViaCdp(endpoint) {
|
|
16
|
+
return loadChromium().connectOverCDP(endpoint);
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
function buildEvaluationExpression(callbackOrExpression, argument) {
|
|
4
20
|
if (typeof callbackOrExpression === 'string') return callbackOrExpression;
|
|
5
21
|
if (typeof callbackOrExpression !== 'function') {
|
|
@@ -31,4 +47,4 @@ async function evaluatePageViaCdp(page, callbackOrExpression, argument) {
|
|
|
31
47
|
}
|
|
32
48
|
}
|
|
33
49
|
|
|
34
|
-
module.exports = { buildEvaluationExpression, evaluatePageViaCdp };
|
|
50
|
+
module.exports = { buildEvaluationExpression, connectBrowserViaCdp, evaluatePageViaCdp };
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
#
|
|
8
8
|
# Inputs: subcommand (status|open|close|toggle|cycle, default status);
|
|
9
9
|
# --cdp-port <port> (required), --ext-id <id>, --agent-dir <dir>,
|
|
10
|
-
# --settle-ms <ms>; env REPO, CDP_PORT, EXT_ID,
|
|
10
|
+
# --settle-ms <ms>, --dapp-url <url>; env REPO, CDP_PORT, EXT_ID,
|
|
11
|
+
# SETTLE_MS, AGENT_DIR, EXTENSION_START_URL.
|
|
11
12
|
# Outputs: status/progress lines on stdout.
|
|
12
13
|
# Exit 0 — action done; 1 — repo/args missing or action failed;
|
|
13
14
|
# 2 — non-numeric port / CDP unreachable.
|
|
@@ -30,7 +31,7 @@ set -euo pipefail
|
|
|
30
31
|
|
|
31
32
|
ACTION="${1:-status}"
|
|
32
33
|
if [ "$ACTION" = "-h" ] || [ "$ACTION" = "--help" ]; then
|
|
33
|
-
echo "Usage: sidepanel-toggle.sh <status|open|close|toggle|cycle> --cdp-port <port> [--ext-id <id>] [--agent-dir <dir>] [--settle-ms <ms>]"
|
|
34
|
+
echo "Usage: sidepanel-toggle.sh <status|open|close|toggle|cycle> --cdp-port <port> [--ext-id <id>] [--agent-dir <dir>] [--settle-ms <ms>] [--dapp-url <url>]"
|
|
34
35
|
exit 0
|
|
35
36
|
fi
|
|
36
37
|
shift || true
|
|
@@ -68,6 +69,7 @@ CDP_PORT="${CDP_PORT:-}"
|
|
|
68
69
|
EXT_ID="${EXT_ID:-}"
|
|
69
70
|
SETTLE_MS="${SETTLE_MS:-10000}"
|
|
70
71
|
AGENT_DIR="${AGENT_DIR:-$SCRIPT_DIR}"
|
|
72
|
+
DAPP_URL="${EXTENSION_START_URL:-https://metamask.github.io/test-dapp/}"
|
|
71
73
|
|
|
72
74
|
while [[ $# -gt 0 ]]; do
|
|
73
75
|
case "$1" in
|
|
@@ -75,8 +77,9 @@ while [[ $# -gt 0 ]]; do
|
|
|
75
77
|
--ext-id) EXT_ID="$2"; shift 2 ;;
|
|
76
78
|
--agent-dir) AGENT_DIR="$2"; shift 2 ;;
|
|
77
79
|
--settle-ms) SETTLE_MS="$2"; shift 2 ;;
|
|
80
|
+
--dapp-url) DAPP_URL="$2"; shift 2 ;;
|
|
78
81
|
-h|--help)
|
|
79
|
-
echo "Usage: sidepanel-toggle.sh <status|open|close|toggle|cycle> --cdp-port <port> [--ext-id <id>] [--agent-dir <dir>] [--settle-ms <ms>]"
|
|
82
|
+
echo "Usage: sidepanel-toggle.sh <status|open|close|toggle|cycle> --cdp-port <port> [--ext-id <id>] [--agent-dir <dir>] [--settle-ms <ms>] [--dapp-url <url>]"
|
|
80
83
|
exit 0
|
|
81
84
|
;;
|
|
82
85
|
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
|
@@ -91,6 +94,10 @@ if ! [[ "$CDP_PORT" =~ ^[0-9]+$ ]]; then
|
|
|
91
94
|
echo "FAIL: CDP port \"$CDP_PORT\" must be numeric" >&2
|
|
92
95
|
exit 2
|
|
93
96
|
fi
|
|
97
|
+
case "$DAPP_URL" in
|
|
98
|
+
http://*|https://*) ;;
|
|
99
|
+
*) echo "FAIL: --dapp-url must use http or https" >&2; exit 1 ;;
|
|
100
|
+
esac
|
|
94
101
|
if ! curl -s -m 3 "http://127.0.0.1:${CDP_PORT}/json/version" >/dev/null 2>&1; then
|
|
95
102
|
echo "FAIL: CDP not reachable on port ${CDP_PORT}" >&2
|
|
96
103
|
exit 2
|
|
@@ -105,7 +112,44 @@ focus_browser() {
|
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
find_sidepanel_id() {
|
|
108
|
-
|
|
115
|
+
local ext_id
|
|
116
|
+
ext_id="$(resolve_ext_id)"
|
|
117
|
+
json_list | EXT_ID="$ext_id" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); ids=[t.get('id','') for t in d if t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/sidepanel.html')]; print(ids[0] if ids else '')"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
find_dapp_id() {
|
|
121
|
+
json_list | DAPP_URL="$DAPP_URL" python3 -c "import json,os,sys; d=json.load(sys.stdin); wanted=os.environ['DAPP_URL'].rstrip('/'); ids=[t.get('id','') for t in d if t.get('type')=='page' and t.get('url','').rstrip('/')==wanted]; print(ids[0] if ids else '')"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
surface_counts() {
|
|
125
|
+
local ext_id
|
|
126
|
+
ext_id="$(resolve_ext_id)"
|
|
127
|
+
json_list | EXT_ID="$ext_id" DAPP_URL="$DAPP_URL" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); wanted=os.environ['DAPP_URL'].rstrip('/'); pages=[t for t in d if t.get('type')=='page']; homes=sum(t.get('url','').startswith('chrome-extension://'+ext+'/home.html') for t in pages); panels=sum(t.get('url','').startswith('chrome-extension://'+ext+'/sidepanel.html') for t in pages); dapps=sum(t.get('url','').rstrip('/')==wanted for t in pages); products=sum(t.get('url','').startswith('chrome-extension://'+ext+'/') for t in pages); print(homes,panels,dapps,products)"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ensure_dapp_tab() {
|
|
131
|
+
[ -n "$(find_dapp_id)" ] && return 0
|
|
132
|
+
local encoded
|
|
133
|
+
encoded="$(DAPP_URL="$DAPP_URL" python3 -c "import os,urllib.parse; print(urllib.parse.quote(os.environ['DAPP_URL'], safe=''))")"
|
|
134
|
+
curl -fsS -X PUT "http://127.0.0.1:${CDP_PORT}/json/new?${encoded}" >/dev/null
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ensure_home_page() {
|
|
138
|
+
local ext_id encoded home_ids first
|
|
139
|
+
ext_id="$(resolve_ext_id)"
|
|
140
|
+
[ -n "$ext_id" ] || { echo "FAIL: could not resolve extension id for CDP ${CDP_PORT}" >&2; return 1; }
|
|
141
|
+
home_ids="$(json_list | EXT_ID="$ext_id" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); print('\\n'.join(t.get('id','') for t in d if t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/home.html')))" )"
|
|
142
|
+
if [ -n "$home_ids" ]; then
|
|
143
|
+
first=1
|
|
144
|
+
while IFS= read -r target_id; do
|
|
145
|
+
[ -n "$target_id" ] || continue
|
|
146
|
+
if [ "$first" -eq 1 ]; then first=0; continue; fi
|
|
147
|
+
curl -fsS "http://127.0.0.1:${CDP_PORT}/json/close/${target_id}" >/dev/null
|
|
148
|
+
done <<< "$home_ids"
|
|
149
|
+
return 0
|
|
150
|
+
fi
|
|
151
|
+
encoded="$(EXT_ID="$ext_id" python3 -c "import os,urllib.parse; print(urllib.parse.quote('chrome-extension://'+os.environ['EXT_ID']+'/home.html#/', safe=''))")"
|
|
152
|
+
curl -fsS -X PUT "http://127.0.0.1:${CDP_PORT}/json/new?${encoded}" >/dev/null
|
|
109
153
|
}
|
|
110
154
|
|
|
111
155
|
resolve_ext_id() {
|
|
@@ -172,6 +216,20 @@ wait_for_sidepanel() {
|
|
|
172
216
|
done
|
|
173
217
|
}
|
|
174
218
|
|
|
219
|
+
wait_for_surface_invariant() {
|
|
220
|
+
local expected="$1" start deadline now home_count panel_count dapp_count product_count
|
|
221
|
+
start="$(now_ms)"
|
|
222
|
+
deadline=$((start + SETTLE_MS))
|
|
223
|
+
while :; do
|
|
224
|
+
read -r home_count panel_count dapp_count product_count <<< "$(surface_counts)"
|
|
225
|
+
if [ "$expected" = "fullscreen" ] && [ "$home_count" -eq 1 ] && [ "$panel_count" -eq 0 ] && [ "$product_count" -eq 1 ]; then return 0; fi
|
|
226
|
+
if [ "$expected" = "sidepanel" ] && [ "$home_count" -eq 0 ] && [ "$panel_count" -eq 1 ] && [ "$dapp_count" -eq 1 ] && [ "$product_count" -eq 1 ]; then return 0; fi
|
|
227
|
+
now="$(now_ms)"
|
|
228
|
+
[ "$now" -lt "$deadline" ] || return 1
|
|
229
|
+
sleep 0.2
|
|
230
|
+
done
|
|
231
|
+
}
|
|
232
|
+
|
|
175
233
|
print_targets() {
|
|
176
234
|
json_list | python3 -c "import json,sys; d=json.load(sys.stdin); [print(f' {t.get(\"type\",\"?\")[:18]:18s} {t.get(\"url\",\"\")[:100]}') for t in d]"
|
|
177
235
|
}
|
|
@@ -179,7 +237,7 @@ print_targets() {
|
|
|
179
237
|
activate_dapp_tab() {
|
|
180
238
|
local target_id
|
|
181
239
|
target_id="$(
|
|
182
|
-
json_list | python3 -c "import json,sys; d=json.load(sys.stdin); pages=[t for t in d if t.get('type')=='page']; target=next((t for t in pages if t.get('url','').
|
|
240
|
+
json_list | DAPP_URL="$DAPP_URL" python3 -c "import json,os,sys; d=json.load(sys.stdin); wanted=os.environ['DAPP_URL'].rstrip('/'); pages=[t for t in d if t.get('type')=='page']; target=next((t for t in pages if t.get('url','').rstrip('/')==wanted), None); print(target.get('id','') if target else '')"
|
|
183
241
|
)"
|
|
184
242
|
if [ -n "$target_id" ] && focus_browser; then
|
|
185
243
|
curl -s "http://127.0.0.1:${CDP_PORT}/json/activate/${target_id}" >/dev/null || true
|
|
@@ -191,7 +249,7 @@ cleanup_sidepanel_tabs() {
|
|
|
191
249
|
ext_id="$(resolve_ext_id)"
|
|
192
250
|
[ -n "$ext_id" ] || return 0
|
|
193
251
|
close_ids="$(
|
|
194
|
-
json_list | EXT_ID="$ext_id" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); has_panel=any(t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/
|
|
252
|
+
json_list | EXT_ID="$ext_id" DAPP_URL="$DAPP_URL" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); wanted=os.environ['DAPP_URL'].rstrip('/'); has_panel=any(t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/sidepanel.html') for t in d); has_dapp=any(t.get('type')=='page' and t.get('url','').rstrip('/')==wanted for t in d); print('\\n'.join(t.get('id','') for t in d if has_panel and has_dapp and t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/home.html')))"
|
|
195
253
|
)"
|
|
196
254
|
if [ -n "$close_ids" ]; then
|
|
197
255
|
while IFS= read -r target_id; do
|
|
@@ -202,6 +260,18 @@ cleanup_sidepanel_tabs() {
|
|
|
202
260
|
activate_dapp_tab
|
|
203
261
|
}
|
|
204
262
|
|
|
263
|
+
dedupe_sidepanel_tabs() {
|
|
264
|
+
local ext_id sidepanel_ids first
|
|
265
|
+
ext_id="$(resolve_ext_id)"
|
|
266
|
+
sidepanel_ids="$(json_list | EXT_ID="$ext_id" python3 -c "import json,os,sys; d=json.load(sys.stdin); ext=os.environ.get('EXT_ID',''); print('\\n'.join(t.get('id','') for t in d if t.get('type')=='page' and t.get('url','').startswith('chrome-extension://'+ext+'/sidepanel.html')))" )"
|
|
267
|
+
first=1
|
|
268
|
+
while IFS= read -r target_id; do
|
|
269
|
+
[ -n "$target_id" ] || continue
|
|
270
|
+
if [ "$first" -eq 1 ]; then first=0; continue; fi
|
|
271
|
+
curl -fsS "http://127.0.0.1:${CDP_PORT}/json/close/${target_id}" >/dev/null
|
|
272
|
+
done <<< "$sidepanel_ids"
|
|
273
|
+
}
|
|
274
|
+
|
|
205
275
|
status_sidepanel() {
|
|
206
276
|
local sp
|
|
207
277
|
sp="$(find_sidepanel_id)"
|
|
@@ -217,15 +287,22 @@ close_sidepanel() {
|
|
|
217
287
|
sp="$(find_sidepanel_id)"
|
|
218
288
|
if [ -z "$sp" ]; then
|
|
219
289
|
echo "[sidepanel] already closed"
|
|
290
|
+
ensure_home_page
|
|
291
|
+
wait_for_surface_invariant fullscreen || { echo "FAIL: fullscreen surface did not converge to one Home target" >&2; return 1; }
|
|
220
292
|
return 0
|
|
221
293
|
fi
|
|
222
294
|
curl -s "http://127.0.0.1:${CDP_PORT}/json/close/${sp}" >/dev/null
|
|
295
|
+
ensure_home_page
|
|
296
|
+
wait_for_surface_invariant fullscreen || { echo "FAIL: fullscreen surface did not converge to one Home target" >&2; return 1; }
|
|
223
297
|
echo "[sidepanel] closed target ${sp}"
|
|
224
298
|
}
|
|
225
299
|
|
|
226
300
|
open_sidepanel() {
|
|
301
|
+
ensure_dapp_tab
|
|
227
302
|
if [ -n "$(find_sidepanel_id)" ]; then
|
|
303
|
+
dedupe_sidepanel_tabs
|
|
228
304
|
cleanup_sidepanel_tabs
|
|
305
|
+
wait_for_surface_invariant sidepanel || { echo "FAIL: sidepanel surface did not converge to one panel and a dapp host" >&2; return 1; }
|
|
229
306
|
echo "[sidepanel] already open"
|
|
230
307
|
return 0
|
|
231
308
|
fi
|
|
@@ -239,13 +316,54 @@ open_sidepanel() {
|
|
|
239
316
|
exit 4
|
|
240
317
|
fi
|
|
241
318
|
|
|
242
|
-
|
|
243
|
-
const
|
|
319
|
+
CDP_PORT="$CDP_PORT" EXT_ID="$ext_id" SCRIPT_DIR="$SCRIPT_DIR" node <<'NODE'
|
|
320
|
+
const path = require('node:path');
|
|
321
|
+
const {
|
|
322
|
+
connectBrowserViaCdp,
|
|
323
|
+
evaluatePageViaCdp,
|
|
324
|
+
} = require(path.join(process.env.SCRIPT_DIR, 'lib/playwright-cdp.cjs'));
|
|
325
|
+
|
|
326
|
+
async function clickVisibleSafeHit(page, selector, successSelector, timeoutMs) {
|
|
327
|
+
const deadline = Date.now() + timeoutMs;
|
|
328
|
+
while (Date.now() < deadline) {
|
|
329
|
+
if (await page.locator(successSelector).isVisible().catch(() => false)) return;
|
|
330
|
+
const candidate = page.locator(selector);
|
|
331
|
+
const count = await candidate.count();
|
|
332
|
+
if (count > 1) throw new Error(`expected one visible ${selector} control, found ${count}`);
|
|
333
|
+
if (count === 1 && await candidate.isVisible().catch(() => false)) {
|
|
334
|
+
try {
|
|
335
|
+
const point = await evaluatePageViaCdp(page, (candidateSelector) => {
|
|
336
|
+
const matches = [...document.querySelectorAll(candidateSelector)];
|
|
337
|
+
if (matches.length !== 1) return null;
|
|
338
|
+
const [element] = matches;
|
|
339
|
+
const rect = element.getBoundingClientRect();
|
|
340
|
+
const points = [
|
|
341
|
+
[rect.left + rect.width / 2, rect.top + rect.height / 2],
|
|
342
|
+
[rect.left + rect.width / 4, rect.top + rect.height / 2],
|
|
343
|
+
[rect.left + rect.width * 3 / 4, rect.top + rect.height / 2],
|
|
344
|
+
[rect.left + rect.width / 2, rect.top + rect.height / 4],
|
|
345
|
+
[rect.left + rect.width / 2, rect.top + rect.height * 3 / 4],
|
|
346
|
+
];
|
|
347
|
+
const safe = points.find(([x, y]) => {
|
|
348
|
+
const hit = document.elementFromPoint(x, y);
|
|
349
|
+
return hit === element || Boolean(hit && element.contains(hit));
|
|
350
|
+
});
|
|
351
|
+
return safe ? { x: safe[0], y: safe[1] } : null;
|
|
352
|
+
}, selector);
|
|
353
|
+
if (point) await page.mouse.click(point.x, point.y);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (!/detached|not attached|execution context was destroyed/iu.test(String(error?.message || error))) throw error;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
await page.waitForTimeout(50);
|
|
359
|
+
}
|
|
360
|
+
throw new Error(`visible ${selector} control did not activate within ${timeoutMs}ms`);
|
|
361
|
+
}
|
|
244
362
|
|
|
245
363
|
(async () => {
|
|
246
364
|
const port = process.env.CDP_PORT;
|
|
247
365
|
const extId = process.env.EXT_ID;
|
|
248
|
-
const browser = await
|
|
366
|
+
const browser = await connectBrowserViaCdp(`http://127.0.0.1:${port}`);
|
|
249
367
|
const context = browser.contexts()[0];
|
|
250
368
|
const page = await context.newPage();
|
|
251
369
|
try {
|
|
@@ -256,11 +374,13 @@ const { chromium } = require('playwright');
|
|
|
256
374
|
if (process.env.MM_HARNESS_FOCUS_BROWSER === '1') {
|
|
257
375
|
await page.bringToFront();
|
|
258
376
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
377
|
+
await clickVisibleSafeHit(
|
|
378
|
+
page,
|
|
379
|
+
'[data-testid="account-options-menu-button"]',
|
|
380
|
+
'[data-testid="global-menu-toggle-view"]',
|
|
381
|
+
5000,
|
|
382
|
+
);
|
|
262
383
|
const toggle = page.locator('[data-testid="global-menu-toggle-view"]');
|
|
263
|
-
await toggle.waitFor({ state: 'visible', timeout: 10000 });
|
|
264
384
|
await toggle.click({ timeout: 5000 });
|
|
265
385
|
} finally {
|
|
266
386
|
if (!page.isClosed()) await page.close();
|
|
@@ -277,7 +397,9 @@ const { chromium } = require('playwright');
|
|
|
277
397
|
NODE
|
|
278
398
|
|
|
279
399
|
if wait_for_sidepanel; then
|
|
400
|
+
dedupe_sidepanel_tabs
|
|
280
401
|
cleanup_sidepanel_tabs
|
|
402
|
+
wait_for_surface_invariant sidepanel || { echo "FAIL: sidepanel surface did not converge to one panel and a dapp host" >&2; return 1; }
|
|
281
403
|
echo "[sidepanel] opened"
|
|
282
404
|
return 0
|
|
283
405
|
fi
|
|
@@ -89,6 +89,70 @@ function assertAndroidReleaseArtifactRuntime(identity, deps = defaultDeps) {
|
|
|
89
89
|
const adb = resolveAdb(deps);
|
|
90
90
|
assertInstalledAndroidReleaseArtifact(identity, deps, adb);
|
|
91
91
|
}
|
|
92
|
+
function readAndroidNativeRuntimeIdentity(identity, deps = defaultDeps) {
|
|
93
|
+
assertAndroidReleaseArtifactRuntime(identity, deps);
|
|
94
|
+
const adb = resolveAdb(deps);
|
|
95
|
+
const buildFingerprint = runChecked(deps, adb, [
|
|
96
|
+
"-s",
|
|
97
|
+
identity.deviceSerial,
|
|
98
|
+
"shell",
|
|
99
|
+
"getprop",
|
|
100
|
+
"ro.build.fingerprint"
|
|
101
|
+
]).trim();
|
|
102
|
+
if (!/^[A-Za-z0-9._+:/=-]{1,512}$/u.test(buildFingerprint)) {
|
|
103
|
+
throw new Error(`Android device ${identity.deviceSerial} returned an invalid build fingerprint.`);
|
|
104
|
+
}
|
|
105
|
+
const bootId = runChecked(deps, adb, [
|
|
106
|
+
"-s",
|
|
107
|
+
identity.deviceSerial,
|
|
108
|
+
"shell",
|
|
109
|
+
"cat",
|
|
110
|
+
"/proc/sys/kernel/random/boot_id"
|
|
111
|
+
]).trim().toLowerCase();
|
|
112
|
+
if (!/^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u.test(bootId)) {
|
|
113
|
+
throw new Error(`Android device ${identity.deviceSerial} returned an invalid boot ID.`);
|
|
114
|
+
}
|
|
115
|
+
const pids = runChecked(deps, adb, [
|
|
116
|
+
"-s",
|
|
117
|
+
identity.deviceSerial,
|
|
118
|
+
"shell",
|
|
119
|
+
"pidof",
|
|
120
|
+
identity.packageId
|
|
121
|
+
]).trim().split(/\s+/u).filter(Boolean);
|
|
122
|
+
if (pids.length !== 1 || !/^\d+$/u.test(pids[0])) {
|
|
123
|
+
throw new Error(`Android package ${identity.packageId} must have exactly one running app process on ${identity.deviceSerial}.`);
|
|
124
|
+
}
|
|
125
|
+
const pid = pids[0];
|
|
126
|
+
const processStat = runChecked(deps, adb, [
|
|
127
|
+
"-s",
|
|
128
|
+
identity.deviceSerial,
|
|
129
|
+
"shell",
|
|
130
|
+
"cat",
|
|
131
|
+
`/proc/${pid}/stat`
|
|
132
|
+
]).trim();
|
|
133
|
+
const close = processStat.lastIndexOf(") ");
|
|
134
|
+
const processFields = close === -1 ? [] : processStat.slice(close + 2).trim().split(/\s+/u);
|
|
135
|
+
const startTimeTicks = processFields[19] ?? "";
|
|
136
|
+
if (!/^\d+$/u.test(startTimeTicks)) {
|
|
137
|
+
throw new Error(`Android package ${identity.packageId} returned an invalid process start time on ${identity.deviceSerial}.`);
|
|
138
|
+
}
|
|
139
|
+
const payload = {
|
|
140
|
+
schemaVersion: 1,
|
|
141
|
+
platform: "android",
|
|
142
|
+
deviceSerial: identity.deviceSerial,
|
|
143
|
+
buildFingerprint,
|
|
144
|
+
bootId,
|
|
145
|
+
packageId: identity.packageId,
|
|
146
|
+
versionName: identity.versionName,
|
|
147
|
+
versionCode: identity.versionCode,
|
|
148
|
+
sha256: identity.sha256,
|
|
149
|
+
appProcess: { pid, startTimeTicks }
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
...payload,
|
|
153
|
+
runtimeSessionId: createHash("sha256").update(JSON.stringify(payload)).digest("hex")
|
|
154
|
+
};
|
|
155
|
+
}
|
|
92
156
|
async function relaunchAndroidReleaseArtifactRuntime(identity, deps = defaultDeps) {
|
|
93
157
|
assertAndroidReleaseArtifactRuntime(identity, deps);
|
|
94
158
|
const adb = resolveAdb(deps);
|
|
@@ -291,5 +355,6 @@ const defaultDeps = {
|
|
|
291
355
|
export {
|
|
292
356
|
assertAndroidReleaseArtifactRuntime,
|
|
293
357
|
installAndroidReleaseArtifact,
|
|
358
|
+
readAndroidNativeRuntimeIdentity,
|
|
294
359
|
relaunchAndroidReleaseArtifactRuntime
|
|
295
360
|
};
|
package/dist/adapters.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { execFile } from "node:child_process";
|
|
3
4
|
import { promisify } from "node:util";
|
|
4
5
|
import { compatibilityMode, fixtureSummary, repoShape } from "./doctor.js";
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
import { withExtensionPage } from "../library/actions/extension/platform/cdp.mjs";
|
|
9
10
|
import { bridgeCommand, evalAsync, evalSync, MOBILE_BRIDGE_ERROR_CODES, selectBridgeStatusEntry, simulatorScreenshot } from "../library/actions/mobile/platform/bridge.mjs";
|
|
10
11
|
import { observeNativeUi } from "../library/actions/mobile/platform/observe-ui.mjs";
|
|
12
|
+
import { nativeAgentDeviceStateDir } from "../library/actions/mobile/platform/native-session-name.mjs";
|
|
11
13
|
import { resolveMobileToolPath } from "../library/actions/mobile/platform/tool-paths.mjs";
|
|
12
14
|
import { resolveWalletImportCredentials, validateWalletImportOptions } from "../library/actions/shared/wallet/import-source.mjs";
|
|
13
15
|
const execFileAsync = promisify(execFile);
|
|
@@ -37,13 +39,13 @@ function simpleAdapter(action, executor) {
|
|
|
37
39
|
}
|
|
38
40
|
const LIVE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
|
|
39
41
|
"metamask.assets.read_visible_state",
|
|
42
|
+
"metamask.assets.open_details",
|
|
40
43
|
"metamask.assets.read_details",
|
|
41
44
|
"metamask.assets.verify_sorting",
|
|
42
45
|
"metamask.assets.set_token_visibility",
|
|
43
46
|
"metamask.assets.import_custom_token",
|
|
44
47
|
"metamask.assets.prepare_send",
|
|
45
48
|
"metamask.assets.review_send",
|
|
46
|
-
"metamask.assets.finish_send",
|
|
47
49
|
"metamask.assets.set_custom_gas",
|
|
48
50
|
"metamask.assets.select_network_scope",
|
|
49
51
|
"metamask.wallet.lock",
|
|
@@ -52,6 +54,7 @@ const LIVE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
52
54
|
"metamask.networks.add_chainlist",
|
|
53
55
|
"metamask.networks.remove_custom",
|
|
54
56
|
"metamask.perps.read_visible_state",
|
|
57
|
+
"metamask.perps.search_markets",
|
|
55
58
|
"metamask.perps.compare_provider_market",
|
|
56
59
|
"metamask.perps.read_eligibility",
|
|
57
60
|
"metamask.perps.select_market_filter",
|
|
@@ -63,6 +66,7 @@ const LIVE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
63
66
|
"metamask.perps.edit_margin",
|
|
64
67
|
"metamask.perps.update_position_tpsl",
|
|
65
68
|
"metamask.perps.read_funds_confirmation",
|
|
69
|
+
"metamask.perps.assert_visible_consistency",
|
|
66
70
|
"metamask.swap_bridge.read_visible_state",
|
|
67
71
|
"metamask.swap_bridge.select_assets",
|
|
68
72
|
"metamask.swap_bridge.set_amount",
|
|
@@ -161,7 +165,7 @@ async function semanticResult(platform, action, node, context, forceLive = false
|
|
|
161
165
|
...output,
|
|
162
166
|
credentialSource: credentials.source,
|
|
163
167
|
credentialSourceName: credentials.sourceName,
|
|
164
|
-
|
|
168
|
+
expectedIdentitySha256: createHash("sha256").update(credentials.expectedAddress).digest("hex")
|
|
165
169
|
}
|
|
166
170
|
};
|
|
167
171
|
}
|
|
@@ -250,45 +254,50 @@ function createMetaMaskSemanticAdapters(platform, declaredCustomActions = [], pr
|
|
|
250
254
|
const bundledActions = [
|
|
251
255
|
...walletActions,
|
|
252
256
|
"metamask.assets.read_visible_state",
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
"metamask.assets.open_details",
|
|
258
|
+
"metamask.assets.read_details",
|
|
259
|
+
"metamask.assets.verify_sorting",
|
|
260
|
+
"metamask.assets.set_token_visibility",
|
|
261
|
+
"metamask.assets.import_custom_token",
|
|
257
262
|
...platform === "extension" ? [
|
|
258
263
|
"metamask.assets.prepare_send",
|
|
259
264
|
"metamask.assets.review_send",
|
|
260
|
-
"metamask.assets.finish_send",
|
|
261
265
|
"metamask.assets.set_custom_gas",
|
|
262
266
|
"metamask.assets.select_network_scope"
|
|
263
267
|
] : [],
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
268
|
+
"metamask.wallet.lock",
|
|
269
|
+
"metamask.networks.read_visible_state",
|
|
270
|
+
"metamask.networks.add_custom",
|
|
271
|
+
...platform === "extension" ? ["metamask.networks.add_chainlist"] : [],
|
|
272
|
+
"metamask.networks.remove_custom",
|
|
267
273
|
"metamask.perps.read_visible_state",
|
|
274
|
+
"metamask.perps.search_markets",
|
|
275
|
+
"metamask.perps.set_market_favorite",
|
|
268
276
|
...platform === "extension" ? ["metamask.perps.compare_provider_market"] : [],
|
|
269
277
|
...platform === "extension" ? ["metamask.perps.read_eligibility"] : [],
|
|
270
278
|
...platform === "extension" ? [
|
|
271
279
|
"metamask.perps.open_position_action",
|
|
272
280
|
"metamask.perps.edit_margin",
|
|
273
|
-
"metamask.perps.update_position_tpsl"
|
|
281
|
+
"metamask.perps.update_position_tpsl",
|
|
282
|
+
"metamask.perps.assert_visible_consistency"
|
|
274
283
|
] : [],
|
|
275
284
|
"metamask.swap_bridge.read_visible_state",
|
|
276
285
|
"metamask.swap_bridge.select_assets",
|
|
277
286
|
"metamask.swap_bridge.set_amount",
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
...platform === "
|
|
287
|
+
"metamask.swap_bridge.set_max_amount",
|
|
288
|
+
"metamask.swap_bridge.set_slippage",
|
|
289
|
+
...platform === "mobile" ? ["metamask.swap_bridge.submit_transaction"] : [],
|
|
281
290
|
...platform === "extension" ? ["metamask.settings.set_basic_functionality"] : [],
|
|
282
291
|
"metamask.perps.read_positions",
|
|
283
292
|
"metamask.perps.ensure_positions",
|
|
284
293
|
"metamask.perps.assert_positions",
|
|
285
294
|
"metamask.perps.place_order",
|
|
286
|
-
"metamask.perps.close_positions",
|
|
295
|
+
...platform === "mobile" || platform === "core" ? ["metamask.perps.close_positions"] : [],
|
|
287
296
|
"metamask.perps.read_orders",
|
|
288
297
|
"metamask.perps.close_orders",
|
|
289
298
|
"metamask.perps.ensure_orders",
|
|
290
299
|
"metamask.perps.assert_orders",
|
|
291
|
-
"metamask.perps.start_state",
|
|
300
|
+
...platform === "mobile" || platform === "core" ? ["metamask.perps.start_state"] : [],
|
|
292
301
|
"metamask.perps.teardown_state",
|
|
293
302
|
"metamask.perps.capture_performance",
|
|
294
303
|
...platform === "extension" || platform === "core" ? ["metamask.perps.read_snapshot"] : [],
|
|
@@ -900,9 +909,10 @@ async function executeNativeProviderAction(action, node, context, createTranspor
|
|
|
900
909
|
}
|
|
901
910
|
const app = platform === "android" ? context.env.ANDROID_PACKAGE_ID ?? process.env.ANDROID_PACKAGE_ID ?? "io.metamask" : context.env.IOS_BUNDLE_ID ?? process.env.IOS_BUNDLE_ID ?? "io.metamask.MetaMask";
|
|
902
911
|
const session = `mm-harness-${process.pid}-${context.nodeId}`.replace(/[^a-zA-Z0-9._-]/gu, "-");
|
|
912
|
+
const stateDir = nativeAgentDeviceStateDir(context.env ?? {}, context.projectRoot, String(device));
|
|
903
913
|
let transport;
|
|
904
914
|
try {
|
|
905
|
-
transport = await createTransport({ platform, device, app, session });
|
|
915
|
+
transport = await createTransport({ platform, device, app, session, stateDir });
|
|
906
916
|
} catch (error) {
|
|
907
917
|
throw new Error(
|
|
908
918
|
`${action} could not start the MetaMask native UI provider. Next: npm install -g @deeeed/metamask-harness@latest`,
|
package/dist/command-contract.js
CHANGED
|
@@ -31,6 +31,12 @@ const RECIPE_RUNTIME = {
|
|
|
31
31
|
"--source-digest": value(),
|
|
32
32
|
"--approve-plan": value()
|
|
33
33
|
};
|
|
34
|
+
const RUN_FUNDING = {
|
|
35
|
+
"--funding-request": value(),
|
|
36
|
+
"--funding-preflight": value(),
|
|
37
|
+
"--funding-release-proof": value(),
|
|
38
|
+
"--funding-balance-proof": value()
|
|
39
|
+
};
|
|
34
40
|
const RUNWAY = {
|
|
35
41
|
"--branch": value(),
|
|
36
42
|
"--default-branch": value(),
|
|
@@ -158,10 +164,11 @@ const PUBLIC_COMMAND_CONTRACTS = {
|
|
|
158
164
|
variadic: { label: "key=value", pattern: /^[^=\s]+=.*/u }
|
|
159
165
|
},
|
|
160
166
|
run: {
|
|
161
|
-
options: options(HELP, JSON, JSON_STREAM, TARGET, ADAPTER, MOBILE_PLATFORM, DEVICE, RUNTIME_PORTS, RECIPE_RUNTIME, {
|
|
167
|
+
options: options(HELP, JSON, JSON_STREAM, TARGET, ADAPTER, MOBILE_PLATFORM, DEVICE, RUNTIME_PORTS, RECIPE_RUNTIME, RUN_FUNDING, {
|
|
162
168
|
"--list": bool(),
|
|
163
169
|
"--plan": bool(),
|
|
164
|
-
"--describe": bool()
|
|
170
|
+
"--describe": bool(),
|
|
171
|
+
"--hud": value(["auto", "show", "hide"])
|
|
165
172
|
}),
|
|
166
173
|
positionals: [{ label: "recipe" }],
|
|
167
174
|
minimumPositionals: 1,
|
|
@@ -232,6 +239,7 @@ const PUBLIC_COMMAND_CONTRACTS = {
|
|
|
232
239
|
"--verify": bool(),
|
|
233
240
|
"--runway": bool(),
|
|
234
241
|
"--watch": bool(),
|
|
242
|
+
"--surface": value(["fullscreen", "sidepanel"]),
|
|
235
243
|
"--sidepanel": bool(),
|
|
236
244
|
"--fullscreen": bool(),
|
|
237
245
|
"--url": value(),
|
package/dist/commands/call.js
CHANGED
|
@@ -208,16 +208,19 @@ async function handleCall(argv) {
|
|
|
208
208
|
const artifactsDir = optionString(options, "artifactsDir") ?? defaultCallArtifactsDir(target, resolvedAction, args);
|
|
209
209
|
recordCommandEvidence(artifactsDir);
|
|
210
210
|
const requestedRuntimeOptions = runtimeOptionsFromCli(options);
|
|
211
|
+
const inheritedSource = process.env.FARMSLOT_RECIPE_SOURCE_TRUST || process.env.FARMSLOT_RECIPE_SOURCE_KIND || process.env.FARMSLOT_RECIPE_SOURCE_NAME || process.env.FARMSLOT_RECIPE_SOURCE_DIGEST;
|
|
211
212
|
const callRuntimeOptions = {
|
|
212
213
|
...requestedRuntimeOptions,
|
|
213
214
|
...librarySources ? { librarySources } : {},
|
|
214
215
|
autoHud: false,
|
|
215
216
|
suppressLibraryResolutionLogs: true,
|
|
216
217
|
stdoutIsMachineContract: json,
|
|
217
|
-
source: requestedRuntimeOptions.source
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
...requestedRuntimeOptions.source ? { source: requestedRuntimeOptions.source } : inheritedSource ? {} : {
|
|
219
|
+
source: {
|
|
220
|
+
kind: "operator",
|
|
221
|
+
trust: "trusted",
|
|
222
|
+
name: "mm-harness call"
|
|
223
|
+
}
|
|
221
224
|
}
|
|
222
225
|
};
|
|
223
226
|
let preflightedExecution;
|