@deeeed/metamask-harness 0.47.4 → 0.49.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 +12 -0
- package/README.md +3 -2
- package/adapters/extension/lib/macos-focus.cjs +2 -2
- package/adapters/extension/live.sh +6 -8
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +1 -0
- package/adapters/shared/ensure-runner-deps.sh +6 -0
- package/dist/adapters/extension/runtime-decision.js +18 -1
- package/dist/adapters/extension/runtime.js +5 -5
- package/dist/adapters/extension/surface.js +1 -0
- package/dist/app-lifecycle.js +3 -1
- package/dist/command-contract.js +2 -0
- package/dist/commands/call.js +1 -8
- package/dist/commands/device-target.js +12 -3
- package/dist/commands/doctor.js +7 -5
- package/dist/commands/fixtures.js +1 -1
- package/dist/commands/help.js +17 -5
- package/dist/commands/launch/index.js +10 -0
- package/dist/commands/manifest.js +3 -1
- package/dist/commands/mobile-device-view.js +13 -3
- package/dist/commands/parse-args.js +2 -0
- package/dist/commands/run-engine.js +29 -4
- package/dist/commands/run.js +1 -1
- package/dist/commands/runtime-launch.js +10 -1
- package/dist/commands/status.js +8 -4
- package/dist/devices.js +31 -9
- package/dist/execution-provenance.js +8 -7
- package/dist/funding-execution-context.js +62 -17
- package/dist/heal-bounds.js +3 -3
- package/dist/live-adapter-contract.js +4 -0
- package/dist/manifest.js +2 -13
- package/dist/metamask-action-validation.js +3 -2
- package/dist/mm-harness-cli.js +4 -2
- package/dist/runner.js +14 -3
- package/docs/RECIPES.md +135 -24
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +3 -2
- package/library/actions/core/perps/_controller.mjs +24 -24
- package/library/actions/core/perps/assert_orders.mjs +3 -5
- package/library/actions/core/perps/assert_positions.mjs +3 -5
- package/library/actions/core/perps/close_orders.mjs +39 -19
- package/library/actions/core/perps/close_positions.mjs +53 -12
- package/library/actions/core/perps/edit_order.mjs +2 -0
- package/library/actions/core/perps/ensure_orders.mjs +3 -4
- package/library/actions/core/perps/ensure_positions.mjs +4 -5
- package/library/actions/core/perps/place_order.mjs +7 -14
- package/library/actions/core/perps/read_account.mjs +2 -0
- package/library/actions/core/perps/read_orders.mjs +2 -0
- package/library/actions/core/perps/read_positions.mjs +3 -1
- package/library/actions/core/perps/read_snapshot.mjs +3 -0
- package/library/actions/core/perps/start_state.mjs +3 -14
- package/library/actions/core/perps/teardown_state.mjs +3 -14
- package/library/actions/core/perps/update_position_tpsl.mjs +13 -19
- package/library/actions/core/wallet/list_accounts.mjs +3 -0
- package/library/actions/extension/analytics/consent.mjs +2 -0
- package/library/actions/extension/analytics/set_consent.mjs +2 -0
- package/library/actions/extension/assets/home-token-identity.mjs +31 -0
- package/library/actions/extension/assets/import_custom_token.mjs +63 -79
- package/library/actions/extension/assets/open_details.mjs +19 -7
- package/library/actions/extension/assets/prepare_send.mjs +1 -0
- package/library/actions/extension/assets/read_details.mjs +62 -29
- package/library/actions/extension/assets/read_visible_state.mjs +5 -1
- package/library/actions/extension/assets/review_send.mjs +1 -0
- package/library/actions/extension/assets/select_network_scope.mjs +17 -9
- package/library/actions/extension/assets/send.mjs +2 -0
- package/library/actions/extension/assets/set_custom_gas.mjs +1 -0
- package/library/actions/extension/assets/set_token_visibility.mjs +86 -17
- package/library/actions/extension/assets/verify_sorting.mjs +63 -14
- package/library/actions/extension/deeplink/open.mjs +1 -0
- package/library/actions/extension/networks/add_chainlist.mjs +2 -0
- package/library/actions/extension/networks/add_custom.mjs +21 -17
- package/library/actions/extension/networks/custom_network.mjs +10 -3
- package/library/actions/extension/networks/read_visible_state.mjs +2 -0
- package/library/actions/extension/networks/remove_custom.mjs +1 -0
- package/library/actions/extension/performance/_navigation-memory.mjs +37 -6
- package/library/actions/extension/performance/compare_idle_navigation_memory.mjs +5 -2
- package/library/actions/extension/performance/measure_detached_dom.mjs +5 -2
- package/library/actions/extension/performance/measure_navigation_memory.mjs +5 -2
- package/library/actions/extension/perps/assert_orders.mjs +1 -0
- package/library/actions/extension/perps/assert_positions.mjs +1 -0
- package/library/actions/extension/perps/assert_visible_consistency.mjs +11 -4
- package/library/actions/extension/perps/close_orders.mjs +4 -1
- package/library/actions/extension/perps/close_positions.mjs +1 -0
- package/library/actions/extension/perps/close_visible_position.mjs +160 -11
- package/library/actions/extension/perps/compare_provider_market.mjs +2 -0
- package/library/actions/extension/perps/edit_margin.mjs +3 -1
- package/library/actions/extension/perps/ensure_orders.mjs +1 -0
- package/library/actions/extension/perps/ensure_positions.mjs +1 -0
- package/library/actions/extension/perps/mutation-receipt.mjs +54 -3
- package/library/actions/extension/perps/open_balance_action.mjs +17 -7
- package/library/actions/extension/perps/open_position_action.mjs +1 -0
- package/library/actions/extension/perps/perps.mjs +550 -69
- package/library/actions/extension/perps/place_order.mjs +1 -0
- package/library/actions/extension/perps/read_eligibility.mjs +1 -0
- package/library/actions/extension/perps/read_funds_confirmation.mjs +10 -0
- package/library/actions/extension/perps/read_orders.mjs +1 -0
- package/library/actions/extension/perps/read_positions.mjs +1 -0
- package/library/actions/extension/perps/read_snapshot.mjs +4 -1
- package/library/actions/extension/perps/read_visible_state.mjs +12 -6
- package/library/actions/extension/perps/search_markets.mjs +8 -2
- package/library/actions/extension/perps/select_activity_filter.mjs +2 -1
- package/library/actions/extension/perps/select_market_filter.mjs +75 -45
- package/library/actions/extension/perps/set_market_favorite.mjs +7 -3
- package/library/actions/extension/perps/state.mjs +2 -0
- package/library/actions/extension/perps/update_position_tpsl.mjs +3 -1
- package/library/actions/extension/perps/visible-mutation-identity.mjs +6 -2
- package/library/actions/extension/platform/cdp.mjs +50 -142
- package/library/actions/extension/settings/set_basic_functionality.mjs +1 -0
- package/library/actions/extension/swap_bridge/quote-state.mjs +56 -0
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +5 -1
- package/library/actions/extension/swap_bridge/select_assets.mjs +12 -1
- package/library/actions/extension/swap_bridge/set_amount.mjs +11 -8
- package/library/actions/extension/swap_bridge/set_max_amount.mjs +18 -18
- package/library/actions/extension/swap_bridge/set_slippage.mjs +1 -9
- package/library/actions/extension/ui/locators.mjs +1 -0
- package/library/actions/extension/ui/navigate.mjs +60 -14
- package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -0
- package/library/actions/extension/wallet/import.mjs +2 -0
- package/library/actions/extension/wallet/list_accounts.mjs +1 -0
- package/library/actions/extension/wallet/lock.mjs +4 -1
- package/library/actions/extension/wallet/read_state.mjs +1 -0
- package/library/actions/extension/wallet/reset.mjs +2 -0
- package/library/actions/extension/wallet/secret-input.mjs +2 -0
- package/library/actions/extension/wallet/select_account.mjs +2 -0
- package/library/actions/extension/wallet/setup.mjs +1 -0
- package/library/actions/extension/wallet/state.mjs +2 -0
- package/library/actions/extension/wallet/visible-session.mjs +2 -0
- package/library/actions/mobile/analytics/consent-settings.mjs +1 -0
- package/library/actions/mobile/analytics/set_consent.mjs +1 -0
- package/library/actions/mobile/app/network-control.mjs +2 -0
- package/library/actions/mobile/app/network.mjs +1 -0
- package/library/actions/mobile/app/network_assert.mjs +1 -0
- package/library/actions/mobile/app/network_capture.mjs +1 -0
- package/library/actions/mobile/assets/import_custom_token.mjs +3 -0
- package/library/actions/mobile/assets/open_details.mjs +1 -0
- package/library/actions/mobile/assets/read_details.mjs +1 -0
- package/library/actions/mobile/assets/read_visible_state.mjs +4 -1
- package/library/actions/mobile/assets/set_token_visibility.mjs +15 -9
- package/library/actions/mobile/assets/verify_sorting.mjs +90 -7
- package/library/actions/mobile/deeplink/open.mjs +1 -0
- package/library/actions/mobile/networks/add_custom.mjs +1 -0
- package/library/actions/mobile/networks/network-management.mjs +23 -15
- package/library/actions/mobile/networks/read_visible_state.mjs +1 -0
- package/library/actions/mobile/networks/remove_custom.mjs +7 -2
- package/library/actions/mobile/perps/assert_orders.mjs +1 -0
- package/library/actions/mobile/perps/assert_positions.mjs +1 -0
- package/library/actions/mobile/perps/capture_performance.mjs +1 -0
- package/library/actions/mobile/perps/close_orders.mjs +1 -0
- package/library/actions/mobile/perps/close_positions.mjs +1 -0
- package/library/actions/mobile/perps/ensure_orders.mjs +1 -0
- package/library/actions/mobile/perps/ensure_positions.mjs +1 -0
- package/library/actions/mobile/perps/measure_homepage_visible.mjs +1 -0
- package/library/actions/mobile/perps/performance-capture.mjs +2 -0
- package/library/actions/mobile/perps/perps.mjs +8 -1
- package/library/actions/mobile/perps/place_order.mjs +1 -0
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +2 -0
- package/library/actions/mobile/perps/read-visible-state-loop.mjs +3 -0
- package/library/actions/mobile/perps/read_orders.mjs +1 -0
- package/library/actions/mobile/perps/read_positions.mjs +1 -0
- package/library/actions/mobile/perps/read_visible_state.mjs +1 -0
- package/library/actions/mobile/perps/search_markets.mjs +1 -0
- package/library/actions/mobile/perps/set_market_favorite.mjs +17 -19
- package/library/actions/mobile/platform/bridge.mjs +16 -4
- package/library/actions/mobile/platform/native-session-name.mjs +1 -0
- package/library/actions/mobile/platform/native-session.mjs +28 -17
- package/library/actions/mobile/platform/observe-ui.mjs +4 -2
- package/library/actions/mobile/platform/tool-paths.mjs +1 -0
- package/library/actions/mobile/swap_bridge/native-session.mjs +1 -0
- package/library/actions/mobile/swap_bridge/read_visible_state.mjs +1 -0
- package/library/actions/mobile/swap_bridge/select_assets.mjs +2 -0
- package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -0
- package/library/actions/mobile/swap_bridge/set_max_amount.mjs +1 -0
- package/library/actions/mobile/swap_bridge/set_slippage.mjs +1 -0
- package/library/actions/mobile/swap_bridge/submit_transaction.mjs +2 -0
- package/library/actions/mobile/ui/locators.mjs +1 -0
- package/library/actions/mobile/ui/native-navigation.mjs +2 -0
- package/library/actions/mobile/ui/navigate.mjs +3 -1
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +2 -0
- package/library/actions/mobile/wallet/home.mjs +1 -0
- package/library/actions/mobile/wallet/import.mjs +2 -0
- package/library/actions/mobile/wallet/list_accounts.mjs +1 -0
- package/library/actions/mobile/wallet/lock.mjs +1 -0
- package/library/actions/mobile/wallet/native-ui.mjs +2 -0
- package/library/actions/mobile/wallet/read_state.mjs +2 -0
- package/library/actions/mobile/wallet/reset-helper.mjs +1 -0
- package/library/actions/mobile/wallet/reset.mjs +1 -0
- package/library/actions/mobile/wallet/select_account.mjs +2 -0
- package/library/actions/mobile/wallet/setup.mjs +2 -0
- package/library/actions/shared/analytics/assert_events.mjs +3 -0
- package/library/actions/shared/analytics/consent.mjs +3 -0
- package/library/actions/shared/analytics/read_events.mjs +2 -0
- package/library/actions/shared/analytics/start_capture.mjs +3 -0
- package/library/actions/shared/app/network-artifact.mjs +2 -0
- package/library/actions/shared/app/network-assert.mjs +2 -0
- package/library/actions/shared/assets/visible-state.mjs +2 -0
- package/library/actions/shared/deeplink/url.mjs +3 -0
- package/library/actions/shared/networks/visible-state.mjs +2 -0
- package/library/actions/shared/perps/visible-state.mjs +16 -3
- package/library/actions/shared/swap-bridge/transaction.mjs +3 -0
- package/library/actions/shared/swap-bridge/visible-state.mjs +2 -0
- package/library/actions/shared/ui/locators.mjs +2 -0
- package/library/actions/shared/wallet/import-source.mjs +3 -0
- package/library/manifests/core.action-manifest.json +34 -2
- package/library/manifests/extension.action-manifest.json +50 -17
- package/library/manifests/mobile.action-manifest.json +11 -10
- package/library/recipes/core/perps/smoke.recipe.json +23 -0
- package/library/recipes/core/perps/snapshot.recipe.json +26 -1
- package/library/recipes/extension/assets/release-custom-network-token-persistence.recipe.json +25 -8
- package/library/recipes/extension/performance/navigation-memory-suite.recipe.json +219 -0
- package/library/recipes/extension/performance/navigation-memory.recipe.json +61 -156
- package/library/recipes/extension/wallet/smoke.recipe.json +17 -8
- package/library/recipes/mobile/app/lifecycle-smoke.recipe.json +89 -0
- package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +31 -6
- package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +5 -4
- package/library/recipes/mobile/wallet/smoke.recipe.json +17 -8
- package/library/recipes/{assets → shared/assets}/release-token-address-import.recipe.json +7 -0
- package/library/recipes/{assets → shared/assets}/release-token-details.recipe.json +2 -2
- package/library/recipes/shared/assets/release-token-hide-readd.recipe.json +91 -0
- package/library/recipes/{assets → shared/assets}/release-token-persistence.recipe.json +3 -0
- package/library/recipes/{assets → shared/assets}/release-token-search-import.recipe.json +4 -1
- package/library/recipes/{assets → shared/assets}/release-token-sorting.recipe.json +13 -2
- package/library/recipes/{swap-bridge → shared/swap-bridge}/release-quote.recipe.json +29 -3
- package/package.json +3 -3
- package/site/assets/help-recipes.json +220 -27
- package/library/recipes/assets/release-token-hide-readd.recipe.json +0 -57
- package/library/recipes/core/perps/clean-market-testnet.recipe.json +0 -44
- package/library/recipes/core/perps/order-lifecycle.recipe.json +0 -82
- package/library/recipes/core/perps/read-markets.recipe.json +0 -36
- package/library/recipes/core/perps/trading-lifecycle.recipe.json +0 -80
- package/library/recipes/extension/perps/release-activity-filters.recipe.json +0 -51
- package/library/recipes/extension/perps/release-funds-flows.recipe.json +0 -76
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +0 -109
- package/library/recipes/extension/perps/release-market-filters.recipe.json +0 -97
- package/library/recipes/extension/perps/release-order-entry.recipe.json +0 -158
- package/library/recipes/extension/perps/release-order-validation.recipe.json +0 -259
- package/library/recipes/extension/perps/release-visible-consistency.recipe.json +0 -47
- package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +0 -76
- package/library/recipes/mobile/app/lifecycle.android-smoke.recipe.json +0 -69
- package/library/recipes/mobile/perps/chase-assert-running.recipe.json +0 -110
- package/library/recipes/mobile/perps/chase-place.recipe.json +0 -145
- package/library/recipes/mobile/perps/chase-terminate.recipe.json +0 -98
- package/library/recipes/mobile/perps/performance.recipe.json +0 -722
- package/library/recipes/mobile/perps/pro-order-setup.recipe.json +0 -111
- package/library/recipes/mobile/perps/pro-order-start-state.recipe.json +0 -53
- package/library/recipes/mobile/perps/scale-assert-orders.recipe.json +0 -126
- package/library/recipes/mobile/perps/scale-place.recipe.json +0 -186
- package/library/recipes/mobile/perps/twap-assert-active.recipe.json +0 -97
- package/library/recipes/mobile/perps/twap-place.recipe.json +0 -146
- package/library/recipes/mobile/runner/action-validation.recipe.json +0 -336
- package/library/recipes/perps/clean-market-testnet.recipe.json +0 -49
- package/library/recipes/perps/lifecycle.recipe.json +0 -144
- package/library/recipes/perps/release-live-market-order.recipe.json +0 -139
- package/library/recipes/perps/release-watchlist.recipe.json +0 -111
- package/library/recipes/swap-bridge/release-max-quote.recipe.json +0 -72
- /package/library/recipes/{assets → shared/assets}/release-token-list.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-details.recipe.json +0 -0
- /package/library/recipes/{perps → shared/perps}/release-market-search.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-custom-slippage.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-max-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-live-swap.recipe.json +0 -0
- /package/library/recipes/{swap-bridge → shared/swap-bridge}/release-surface.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/import.recipe.json +0 -0
- /package/library/recipes/{wallet → shared/wallet}/reset-import.recipe.json +0 -0
package/dist/commands/status.js
CHANGED
|
@@ -37,6 +37,8 @@ async function handleStatus({ options }) {
|
|
|
37
37
|
const next = surface.hints.relaunch;
|
|
38
38
|
const deviceView = adapter === "mobile" ? mobileDeviceView(allDevices) : null;
|
|
39
39
|
const devices = deviceView?.devices ?? [];
|
|
40
|
+
const discovery = deviceView?.deviceDiscoveryErrors.length ? { deviceDiscoveryErrors: deviceView.deviceDiscoveryErrors } : {};
|
|
41
|
+
const discoveryNext = discovery.deviceDiscoveryErrors?.[0]?.userAction;
|
|
40
42
|
if (json) {
|
|
41
43
|
const doProbe = !fast && adapter === "mobile" && deviceView !== null && deviceView.allConnectedDevices.length > 0;
|
|
42
44
|
if (doProbe) {
|
|
@@ -49,9 +51,10 @@ async function handleStatus({ options }) {
|
|
|
49
51
|
adapter,
|
|
50
52
|
target,
|
|
51
53
|
devices: liveView.devicesWithLive,
|
|
54
|
+
...discovery,
|
|
52
55
|
...liveView.additionalReachableDevices.length > 0 ? { additionalReachableDevices: liveView.additionalReachableDevices } : {},
|
|
53
56
|
...liveView.androidPortHints.length > 0 ? { androidPortHints: liveView.androidPortHints } : {},
|
|
54
|
-
next: nextForLive(next, liveView.liveMap)
|
|
57
|
+
next: discoveryNext ?? nextForLive(next, liveView.liveMap)
|
|
55
58
|
},
|
|
56
59
|
null,
|
|
57
60
|
2
|
|
@@ -67,8 +70,9 @@ async function handleStatus({ options }) {
|
|
|
67
70
|
adapter,
|
|
68
71
|
target,
|
|
69
72
|
devices,
|
|
73
|
+
...discovery,
|
|
70
74
|
...runtime ? { runtime } : {},
|
|
71
|
-
next: runtime ? nextForRuntime(surface.hints.launch, surface.hints.relaunch, runtime) : next
|
|
75
|
+
next: discoveryNext ?? (runtime ? nextForRuntime(surface.hints.launch, surface.hints.relaunch, runtime) : next)
|
|
72
76
|
},
|
|
73
77
|
null,
|
|
74
78
|
2
|
|
@@ -85,13 +89,13 @@ async function handleStatus({ options }) {
|
|
|
85
89
|
if (liveView.additionalReachableDevices.length > 0) renderAdditionalReachableDevices(liveView.additionalReachableDevices, out);
|
|
86
90
|
renderMobileLiveBlock(devices, liveView.liveMap, out);
|
|
87
91
|
renderAndroidPortHints(liveView.androidPortHints, out);
|
|
88
|
-
console.log(`${out("label", "Next:")} ${out("cmd", nextForLive(next, liveView.liveMap))}`);
|
|
92
|
+
console.log(`${out("label", "Next:")} ${out("cmd", discoveryNext ?? nextForLive(next, liveView.liveMap))}`);
|
|
89
93
|
} else if (!fast && adapter === "extension") {
|
|
90
94
|
const runtime = await surface.runtimeStatus(target);
|
|
91
95
|
renderRuntimeStatus(runtime, out);
|
|
92
96
|
console.log(`${out("label", "Next:")} ${out("cmd", nextForRuntime(surface.hints.launch, surface.hints.relaunch, runtime))}`);
|
|
93
97
|
} else {
|
|
94
|
-
console.log(`${out("label", "Next:")} ${out("cmd", next)}`);
|
|
98
|
+
console.log(`${out("label", "Next:")} ${out("cmd", discoveryNext ?? next)}`);
|
|
95
99
|
}
|
|
96
100
|
return EXIT.ok;
|
|
97
101
|
}
|
package/dist/devices.js
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { resolveMobileToolPath } from "../library/actions/mobile/platform/tool-paths.mjs";
|
|
3
|
-
function
|
|
3
|
+
function reportProbeFailure(errors, platform, error) {
|
|
4
|
+
const code = error?.code;
|
|
5
|
+
if (code === "ENOENT") return;
|
|
6
|
+
const reason = code === "ETIMEDOUT" ? "timed out after 5 seconds" : "failed";
|
|
7
|
+
const probeCode = code ?? error?.status;
|
|
8
|
+
errors.push({
|
|
9
|
+
platform,
|
|
10
|
+
message: `${platform} device discovery ${reason}; device availability is unknown.`,
|
|
11
|
+
userAction: platform === "ios" ? "xcrun simctl list devices booted -j" : "adb devices -l",
|
|
12
|
+
...probeCode !== void 0 ? { probeCode } : {}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function listConnectedDevices(platform, errors = []) {
|
|
4
16
|
const devices = [];
|
|
5
|
-
if (!platform || platform === "android") devices.push(...listAndroidDevices());
|
|
6
|
-
if (!platform || platform === "ios") devices.push(...listIosSimulators());
|
|
17
|
+
if (!platform || platform === "android") devices.push(...listAndroidDevices(errors));
|
|
18
|
+
if (!platform || platform === "ios") devices.push(...listIosSimulators("booted", errors));
|
|
7
19
|
return devices;
|
|
8
20
|
}
|
|
9
|
-
function findAvailableIosSimulatorById(id) {
|
|
10
|
-
for (const simulator of listIosSimulators("available")) {
|
|
21
|
+
function findAvailableIosSimulatorById(id, errors = []) {
|
|
22
|
+
for (const simulator of listIosSimulators("available", errors)) {
|
|
11
23
|
if (simulator.id === id) return simulator;
|
|
12
24
|
}
|
|
13
25
|
return void 0;
|
|
14
26
|
}
|
|
15
|
-
function listAndroidDevices() {
|
|
27
|
+
function listAndroidDevices(errors) {
|
|
16
28
|
const adbPath = resolveMobileToolPath("adb");
|
|
17
29
|
if (!adbPath) return [];
|
|
18
30
|
let output;
|
|
@@ -22,7 +34,12 @@ function listAndroidDevices() {
|
|
|
22
34
|
timeout: 5e3,
|
|
23
35
|
stdio: ["ignore", "pipe", "ignore"]
|
|
24
36
|
});
|
|
25
|
-
} catch {
|
|
37
|
+
} catch (error) {
|
|
38
|
+
reportProbeFailure(errors, "android", error);
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
if (!output.trimStart().startsWith("List of devices attached")) {
|
|
42
|
+
errors.push({ platform: "android", message: "Android device discovery returned an invalid response; device availability is unknown.", userAction: "adb devices -l" });
|
|
26
43
|
return [];
|
|
27
44
|
}
|
|
28
45
|
const devices = [];
|
|
@@ -39,7 +56,7 @@ function listAndroidDevices() {
|
|
|
39
56
|
}
|
|
40
57
|
return devices;
|
|
41
58
|
}
|
|
42
|
-
function listIosSimulators(scope
|
|
59
|
+
function listIosSimulators(scope, errors) {
|
|
43
60
|
let output;
|
|
44
61
|
try {
|
|
45
62
|
output = execFileSync("xcrun", ["simctl", "list", "devices", scope, "-j"], {
|
|
@@ -47,13 +64,18 @@ function listIosSimulators(scope = "booted") {
|
|
|
47
64
|
timeout: 5e3,
|
|
48
65
|
stdio: ["ignore", "pipe", "ignore"]
|
|
49
66
|
});
|
|
50
|
-
} catch {
|
|
67
|
+
} catch (error) {
|
|
68
|
+
reportProbeFailure(errors, "ios", error);
|
|
51
69
|
return [];
|
|
52
70
|
}
|
|
53
71
|
let parsed;
|
|
54
72
|
try {
|
|
55
73
|
parsed = JSON.parse(output);
|
|
74
|
+
if (!parsed?.devices || typeof parsed.devices !== "object" || Array.isArray(parsed.devices) || Object.values(parsed.devices).some((devices2) => !Array.isArray(devices2) || devices2.some((device) => !device || typeof device.udid !== "string"))) {
|
|
75
|
+
throw new Error("Invalid simctl device inventory");
|
|
76
|
+
}
|
|
56
77
|
} catch {
|
|
78
|
+
errors.push({ platform: "ios", message: "iOS device discovery returned an invalid response; device availability is unknown.", userAction: `xcrun simctl list devices ${scope} -j` });
|
|
57
79
|
return [];
|
|
58
80
|
}
|
|
59
81
|
const devices = [];
|
|
@@ -141,14 +141,15 @@ function sourceSnapshot(root, adapter, excludedRoots = [], includedRoots = []) {
|
|
|
141
141
|
"--",
|
|
142
142
|
...pathspecs
|
|
143
143
|
]);
|
|
144
|
+
const head = gitBuffer(git.topLevel, ["rev-parse", "HEAD"]);
|
|
144
145
|
return {
|
|
145
|
-
head:
|
|
146
|
+
head: head.toString("utf8").trim() || null,
|
|
146
147
|
status,
|
|
147
|
-
sourceFingerprint: adapter === "mobile" && path.resolve(root) === git.topLevel ? mobileSourceFingerprint(root) : nestedGitSourceFingerprint(root, git, pathspecs, excludedRoots, includedRoots)
|
|
148
|
+
sourceFingerprint: adapter === "mobile" && path.resolve(root) === git.topLevel ? mobileSourceFingerprint(root) : nestedGitSourceFingerprint(root, git, pathspecs, excludedRoots, includedRoots, head)
|
|
148
149
|
};
|
|
149
150
|
}
|
|
150
|
-
function nestedGitSourceFingerprint(root, git, pathspecs, excludedRoots, includedRoots) {
|
|
151
|
-
const gitFingerprint = gitSourceFingerprint(git.topLevel, pathspecs);
|
|
151
|
+
function nestedGitSourceFingerprint(root, git, pathspecs, excludedRoots, includedRoots, head) {
|
|
152
|
+
const gitFingerprint = gitSourceFingerprint(git.topLevel, pathspecs, head);
|
|
152
153
|
if (git.pathspec === "." && includedRoots.length === 0) return gitFingerprint;
|
|
153
154
|
return createHash("sha256").update(gitFingerprint).update("\0nested-source\0").update(directoryFingerprint(root, excludedRoots, includedRoots)).digest("hex");
|
|
154
155
|
}
|
|
@@ -169,16 +170,16 @@ function gitContext(root) {
|
|
|
169
170
|
return null;
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
|
-
function gitSourceFingerprint(topLevel, pathspecs) {
|
|
173
|
+
function gitSourceFingerprint(topLevel, pathspecs, head) {
|
|
173
174
|
const hash = createHash("sha256");
|
|
174
|
-
hash.update(
|
|
175
|
+
hash.update(head);
|
|
175
176
|
hash.update("\0diff\0");
|
|
176
177
|
hash.update(
|
|
177
178
|
gitBuffer(topLevel, [
|
|
178
179
|
"diff",
|
|
179
180
|
"--no-ext-diff",
|
|
180
181
|
"--binary",
|
|
181
|
-
"
|
|
182
|
+
head.toString("utf8").trim(),
|
|
182
183
|
"--",
|
|
183
184
|
...pathspecs
|
|
184
185
|
])
|
|
@@ -4,6 +4,7 @@ import { chmod, lstat, mkdir, open, readFile, readdir, realpath } from "node:fs/
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { digestRecipeDocument } from "@farmslot/protocol";
|
|
7
|
+
import { recipeRuntimeDir } from "./paths.js";
|
|
7
8
|
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
8
9
|
const INTEGER = /^(0|[1-9][0-9]{0,39})$/u;
|
|
9
10
|
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
@@ -35,7 +36,8 @@ const CLAIM_DIFF_FIELDS = [
|
|
|
35
36
|
];
|
|
36
37
|
const trustedActionContexts = /* @__PURE__ */ new WeakSet();
|
|
37
38
|
const fundingSources = /* @__PURE__ */ new WeakMap();
|
|
38
|
-
const CURRENT_FUNDING_REGISTRY_SHA256 = "
|
|
39
|
+
const CURRENT_FUNDING_REGISTRY_SHA256 = "d4416e0e8a812a0e2f1798ae6f901a5820f7ee0b9783cfa5d515d0c7863ea80e";
|
|
40
|
+
const PREVIOUS_FUNDING_REGISTRY_SHA256 = "7e79ee2a5f7bdde0a5548208cb2eab55881575cc7d826ee5f7acdbf15d49609c";
|
|
39
41
|
const FROZEN_G005_FUNDING_REGISTRY_SHA256 = "c2230bc02fa00d6902915d1cb81bcc08389ea7e399ee9618f26fb0d9fc00b291";
|
|
40
42
|
const LIVE_PERPS_TINY_REGISTRY = {
|
|
41
43
|
"live-perps-tiny": {
|
|
@@ -57,8 +59,15 @@ const LIVE_PERPS_TINY_REGISTRY = {
|
|
|
57
59
|
};
|
|
58
60
|
const TRUSTED_FUNDING_REGISTRIES = {
|
|
59
61
|
[CURRENT_FUNDING_REGISTRY_SHA256]: LIVE_PERPS_TINY_REGISTRY,
|
|
62
|
+
[PREVIOUS_FUNDING_REGISTRY_SHA256]: LIVE_PERPS_TINY_REGISTRY,
|
|
60
63
|
[FROZEN_G005_FUNDING_REGISTRY_SHA256]: LIVE_PERPS_TINY_REGISTRY
|
|
61
64
|
};
|
|
65
|
+
const SOURCE_DEV_TESTNET_BUDGETS = /* @__PURE__ */ new Set([
|
|
66
|
+
"perps-market-order:usdc-base-units",
|
|
67
|
+
"perps-limit-order:usdc-base-units",
|
|
68
|
+
"perps-cancel-order:usdc-base-units",
|
|
69
|
+
"perps-close-position:usdc-base-units"
|
|
70
|
+
]);
|
|
62
71
|
const OPERATION_ACTION = /* @__PURE__ */ new Map([
|
|
63
72
|
["perps-market-order", "metamask.perps.place_order"],
|
|
64
73
|
["perps-limit-order", "metamask.perps.place_order"],
|
|
@@ -99,7 +108,7 @@ async function loadTrustedMutationBase(options) {
|
|
|
99
108
|
fundingBalanceProof
|
|
100
109
|
);
|
|
101
110
|
const identity = await validateFundingDocuments(documents);
|
|
102
|
-
const currentReleaseRuntime = options.testIo?.currentReleaseRuntime ?? releaseRuntimeValidator(path.resolve(options.projectRoot));
|
|
111
|
+
const currentReleaseRuntime = options.testIo?.currentReleaseRuntime ?? releaseRuntimeValidator(path.resolve(options.projectRoot), identity.proofLane);
|
|
103
112
|
const currentRelease = await currentReleaseRuntime(false, identity.cdpPort);
|
|
104
113
|
if (currentRelease.artifactSha256 !== identity.subjectDigest || currentRelease.treeSha256 !== identity.releaseTreeSha256) {
|
|
105
114
|
throw new Error("Runner-owned current release artifact does not match the funding release proof.");
|
|
@@ -162,6 +171,7 @@ async function authorizeTrustedMutationPlan(base, plan, testIo = {}) {
|
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
173
|
if (actual.size === 0) throw new Error("Funding documents require a resolved external-mutation plan.");
|
|
174
|
+
await mkdir(source.artifactsDir, { recursive: true, mode: 448 });
|
|
165
175
|
const artifactsIdentitySha256 = await directoryIdentity(source.artifactsDir);
|
|
166
176
|
const operationCounts = [...expected.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([action, count]) => ({ action, count }));
|
|
167
177
|
const runId = sha256(canonical({
|
|
@@ -719,14 +729,27 @@ async function validateFundingEvidenceChain(documents, identity) {
|
|
|
719
729
|
if (policy.required !== true || policy.registrySha256 !== identity.registrySha256 || policy.profileId !== identity.profileId || policy.domain !== identity.domain) {
|
|
720
730
|
throw new Error("Funding release proof does not bind the runner-trusted funding policy.");
|
|
721
731
|
}
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
732
|
+
const sourceDev = identity.proofLane === "source-dev";
|
|
733
|
+
let releaseTreeSha256;
|
|
734
|
+
if (sourceDev) {
|
|
735
|
+
const source = requireRecord(releaseProof.source, "funding source-dev proof source");
|
|
736
|
+
const workingTree = requireRecord(source.workingTree, "funding source-dev working tree");
|
|
737
|
+
const build = requireRecord(source.build, "funding source-dev build");
|
|
738
|
+
if (identity.profileId !== "live-perps-tiny" || identity.domain !== "perps" || "artifact" in releaseProof || !/^[a-f0-9]{40}$/u.test(String(source.commit ?? "")) || !["clean", "dirty"].includes(String(workingTree.state)) || !SHA256.test(String(workingTree.statusSha256 ?? "")) || (workingTree.state === "clean" ? workingTree.statusSha256 !== sha256("") || "diffSha256" in workingTree : !SHA256.test(String(workingTree.diffSha256 ?? ""))) || build.outputSha256 !== identity.subjectDigest || !Array.isArray(build.instrumentation) || build.instrumentation.some((item) => typeof item !== "string" || !item.trim()) || releaseProof.compatibilityOnly !== true || releaseProof.releaseRecommendationEligible !== false) {
|
|
739
|
+
throw new Error("Funding source-dev proof requires bound build provenance and compatibility-only evidence.");
|
|
740
|
+
}
|
|
741
|
+
releaseTreeSha256 = String(build.outputSha256);
|
|
742
|
+
} else {
|
|
743
|
+
const artifact = requireRecord(releaseProof.artifact, "funding release proof artifact");
|
|
744
|
+
if (artifact.sha256 !== identity.subjectDigest || !SHA256.test(String(artifact.treeSha256 ?? ""))) {
|
|
745
|
+
throw new Error("Funding release proof artifact identity is invalid.");
|
|
746
|
+
}
|
|
747
|
+
releaseTreeSha256 = String(artifact.treeSha256);
|
|
725
748
|
}
|
|
726
749
|
const runtime = requireRecord(releaseProof.runtime, "funding release proof runtime");
|
|
727
750
|
const extensionRuntime = requireRecord(runtime.extension, "funding release proof Extension runtime");
|
|
728
|
-
if (identity.client !== "extension" || identity.proofLane !== "official-unmodified" || runtime.fixtures !== false || runtime.instrumentation !== false || extensionRuntime.cleanProfile !== true
|
|
729
|
-
throw new Error("Funding
|
|
751
|
+
if (identity.client !== "extension" || extensionRuntime.runtimeTreeSha256 !== releaseTreeSha256 || (sourceDev ? typeof runtime.fixtures !== "boolean" || typeof runtime.instrumentation !== "boolean" || typeof extensionRuntime.cleanProfile !== "boolean" || typeof extensionRuntime.profileId !== "string" || !extensionRuntime.profileId.trim() : identity.proofLane !== "official-unmodified" || runtime.fixtures !== false || runtime.instrumentation !== false || extensionRuntime.cleanProfile !== true)) {
|
|
752
|
+
throw new Error("Funding proof does not match its permitted Extension runtime lane.");
|
|
730
753
|
}
|
|
731
754
|
const onboarding = requireRecord(releaseProof.walletOnboarding, "funding release proof walletOnboarding");
|
|
732
755
|
const onboardingRuntime = requireRecord(onboarding.runtime, "funding release proof wallet runtime");
|
|
@@ -837,8 +860,8 @@ async function validateFundingEvidenceChain(documents, identity) {
|
|
|
837
860
|
const onboardingWallet = successfulWalletOutput(
|
|
838
861
|
onboardingTrace,
|
|
839
862
|
"funding release onboarding evidence",
|
|
840
|
-
"ui-import",
|
|
841
|
-
"extension-visible-onboarding-import"
|
|
863
|
+
sourceDev ? "ui-visible-session" : "ui-import",
|
|
864
|
+
sourceDev ? "extension-visible-wallet-session" : "extension-visible-onboarding-import"
|
|
842
865
|
);
|
|
843
866
|
const onboardingOrigin = extensionOrigin(onboardingWallet.targetUrl, "funding release onboarding wallet");
|
|
844
867
|
if (onboardingWallet.runtimeSessionId !== onboardingRuntime.runtimeSessionId || onboardingWallet.cdpPort !== onboardingRuntime.cdpPort || onboardingWallet.cdpTargetId !== onboardingRuntime.cdpTargetId || extensionOrigin(onboardingRuntime.targetUrl, "funding release onboarding runtime") !== onboardingOrigin) {
|
|
@@ -972,7 +995,7 @@ async function validateFundingEvidenceChain(documents, identity) {
|
|
|
972
995
|
evidenceDocument.runtimeSessionId,
|
|
973
996
|
"funding balance evidence runtimeSessionId"
|
|
974
997
|
);
|
|
975
|
-
if (evidenceDocument.selectedIdentitySha256 !== onboardingWallet.selectedIdentitySha256 || wallet.selectedIdentitySha256 !== onboardingWallet.selectedIdentitySha256 || wallet.runtimeSessionId !== runtimeSessionId || wallet.cdpPort !== onboardingWallet.cdpPort || extensionOrigin(wallet.targetUrl, "funding balance wallet") !== onboardingOrigin) {
|
|
998
|
+
if (evidenceDocument.selectedIdentitySha256 !== onboardingWallet.selectedIdentitySha256 || wallet.selectedIdentitySha256 !== onboardingWallet.selectedIdentitySha256 || wallet.runtimeSessionId !== runtimeSessionId || sourceDev && (wallet.runtimeSessionId !== onboardingWallet.runtimeSessionId || wallet.cdpTargetId !== onboardingWallet.cdpTargetId) || wallet.cdpPort !== onboardingWallet.cdpPort || extensionOrigin(wallet.targetUrl, "funding balance wallet") !== onboardingOrigin) {
|
|
976
999
|
throw new Error("Funding balance evidence wallet or runtime is not anchored to release onboarding.");
|
|
977
1000
|
}
|
|
978
1001
|
const currentRuntime = {
|
|
@@ -987,7 +1010,7 @@ async function validateFundingEvidenceChain(documents, identity) {
|
|
|
987
1010
|
}
|
|
988
1011
|
balanceRuntime = currentRuntime;
|
|
989
1012
|
const visibleState = requireRecord(evidenceDocument.visibleState, "funding balance evidence visibleState");
|
|
990
|
-
if (visibleState.perpsAvailableMarginBaseUnits !== actualBaseUnits || visibleBalanceBaseUnits(trace, emittedNodes) !== actualBaseUnits) {
|
|
1013
|
+
if (visibleState.perpsAvailableMarginBaseUnits !== actualBaseUnits || visibleBalanceBaseUnits(trace, emittedNodes, currentRuntime) !== actualBaseUnits) {
|
|
991
1014
|
throw new Error("Funding balance actual value does not match its visible trace.");
|
|
992
1015
|
}
|
|
993
1016
|
if (!exactTerminalPass(recipe, trace, summary, artifactManifest)) {
|
|
@@ -999,13 +1022,26 @@ async function validateFundingEvidenceChain(documents, identity) {
|
|
|
999
1022
|
return {
|
|
1000
1023
|
requirements,
|
|
1001
1024
|
runtime: balanceRuntime,
|
|
1002
|
-
releaseTreeSha256
|
|
1025
|
+
releaseTreeSha256
|
|
1003
1026
|
};
|
|
1004
1027
|
}
|
|
1005
|
-
function releaseRuntimeValidator(projectRoot) {
|
|
1028
|
+
function releaseRuntimeValidator(projectRoot, proofLane) {
|
|
1006
1029
|
return async (requireLive, cdpPort) => {
|
|
1007
1030
|
const { extensionRuntimeReusable } = await import("./commands/launch/extension.js");
|
|
1008
1031
|
const { releaseArtifactState, runtimeDistCheck } = await import("./adapters/extension/runtime-decision.js");
|
|
1032
|
+
if (proofLane === "source-dev") {
|
|
1033
|
+
const { extensionTreeSha256 } = await import("./adapters/extension/artifact-integrity.js");
|
|
1034
|
+
const configuredPort = Number(process.env.CDP_PORT ?? process.env.RECIPE_CDP_PORT);
|
|
1035
|
+
if (configuredPort !== cdpPort || !await extensionRuntimeReusable(projectRoot, { status: "none" })) {
|
|
1036
|
+
throw new Error("Source-dev funding requires the current harness-owned Extension runtime.");
|
|
1037
|
+
}
|
|
1038
|
+
const treeSha256 = extensionTreeSha256(path.join(
|
|
1039
|
+
projectRoot,
|
|
1040
|
+
recipeRuntimeDir(),
|
|
1041
|
+
process.env.RECIPE_RUNTIME_DIST_DIR || "runtime-dist"
|
|
1042
|
+
));
|
|
1043
|
+
return { artifactSha256: treeSha256, treeSha256 };
|
|
1044
|
+
}
|
|
1009
1045
|
const artifact = releaseArtifactState(projectRoot);
|
|
1010
1046
|
if (artifact.status !== "valid" || runtimeDistCheck(projectRoot, artifact).status !== "fresh" || !artifact.sha256 || !artifact.treeSha256) {
|
|
1011
1047
|
throw new Error("Runner-owned current release artifact is invalid or stale.");
|
|
@@ -1058,11 +1094,17 @@ function extensionOrigin(value, label) {
|
|
|
1058
1094
|
throw new Error(`${label} Extension origin is invalid.`);
|
|
1059
1095
|
}
|
|
1060
1096
|
}
|
|
1061
|
-
function visibleBalanceBaseUnits(trace, emittedNodes) {
|
|
1097
|
+
function visibleBalanceBaseUnits(trace, emittedNodes, runtime) {
|
|
1062
1098
|
const entries = Array.isArray(trace.entries) ? trace.entries : [];
|
|
1063
1099
|
const matches = entries.filter((entry) => isRecord(entry) && entry.action === "metamask.perps.read_visible_state" && entry.ok === true && isRecord(entry.output) && entry.output.action === entry.action && entry.output.surface === "home" && entry.output.source === "visible-ui" && isRecord(entry.output.balance) && entry.output.balance.visible === true && isRecord(emittedNodes.get(String(entry.nodeId))) && emittedNodes.get(String(entry.nodeId))?.surface === "home");
|
|
1064
1100
|
const matchEntry = matches.at(-1);
|
|
1065
1101
|
if (!matchEntry) throw new Error("Funding balance evidence requires a visible Perps Home balance observation.");
|
|
1102
|
+
for (const entry of matches) {
|
|
1103
|
+
const output = requireRecord(entry.output, "funding balance observation");
|
|
1104
|
+
if (output.runtimeSessionId !== runtime.runtimeSessionId || output.cdpPort !== runtime.cdpPort || extensionOrigin(output.targetUrl, "funding balance observation") !== runtime.extensionOrigin) {
|
|
1105
|
+
throw new Error("Funding balance observation is not from its anchored wallet runtime.");
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1066
1108
|
const label = String(matchEntry.output.balance.label ?? "");
|
|
1067
1109
|
const match = /(?:^|\s)\$([0-9]+(?:\.[0-9]{1,2})?)(?:\s|$)/u.exec(label);
|
|
1068
1110
|
if (!match) throw new Error("Funding balance evidence visible amount is invalid.");
|
|
@@ -1253,14 +1295,16 @@ function parsePreflightRequirements(value) {
|
|
|
1253
1295
|
if (!Array.isArray(value)) throw new Error("Funding preflight requirements must be an array.");
|
|
1254
1296
|
return value.map((entry, index) => {
|
|
1255
1297
|
const record = requireRecord(entry, `funding preflight requirements[${index}]`);
|
|
1298
|
+
const evidence = requireRecord(record.evidence, `funding preflight requirements[${index}].evidence`);
|
|
1299
|
+
const wrapper = requireRecord(evidence.wrapper, `funding preflight requirements[${index}].evidence.wrapper`);
|
|
1256
1300
|
return {
|
|
1257
1301
|
kind: requireIdentifier(record.kind, `funding preflight requirements[${index}].kind`),
|
|
1258
1302
|
id: requireIdentifier(record.id, `funding preflight requirements[${index}].id`),
|
|
1259
1303
|
minBaseUnits: requireInteger(record.minBaseUnits, `funding preflight requirements[${index}].minBaseUnits`),
|
|
1260
1304
|
actualBaseUnits: requireInteger(record.actualBaseUnits, `funding preflight requirements[${index}].actualBaseUnits`),
|
|
1261
1305
|
evidenceSha256: requireIdentifier(
|
|
1262
|
-
|
|
1263
|
-
`funding preflight requirements[${index}].
|
|
1306
|
+
wrapper.sha256,
|
|
1307
|
+
`funding preflight requirements[${index}].evidence.wrapper.sha256`,
|
|
1264
1308
|
true
|
|
1265
1309
|
)
|
|
1266
1310
|
};
|
|
@@ -1295,7 +1339,8 @@ function validateTrustedFundingRegistry(identity, operations, budgets) {
|
|
|
1295
1339
|
for (const [key, aggregate] of requested) {
|
|
1296
1340
|
const trusted = domain.budgets.get(key);
|
|
1297
1341
|
const supplied = suppliedBudgets.get(key);
|
|
1298
|
-
|
|
1342
|
+
const maximum = BigInt(identity.registrySha256 === CURRENT_FUNDING_REGISTRY_SHA256 && identity.client === "extension" && identity.proofLane === "source-dev" && SOURCE_DEV_TESTNET_BUDGETS.has(key) ? "20000000" : trusted?.maxBaseUnits ?? "0");
|
|
1343
|
+
if (!trusted || !supplied || BigInt(supplied.maxBaseUnits) > maximum || supplied.maxCount > trusted.maxCount || aggregate.maxAmountBaseUnits > maximum || aggregate.count > trusted.maxCount || aggregate.amountBaseUnits > maximum * BigInt(trusted.maxCount)) {
|
|
1299
1344
|
throw new Error(`Funding operation ${aggregate.operation} is not bounded by the runner-trusted funding registry.`);
|
|
1300
1345
|
}
|
|
1301
1346
|
}
|
package/dist/heal-bounds.js
CHANGED
|
@@ -63,7 +63,7 @@ async function ensureOverlay(adapter, target, heal, state, json) {
|
|
|
63
63
|
function classifyFailure(output) {
|
|
64
64
|
if (/SCREENSHOT_PROTECTED|screenshot is protected by FLAG_SECURE/iu.test(output)) return "screenshot-protected";
|
|
65
65
|
if (/CDP_TIMEOUT|Mobile CDP bridge command timed out|NO_TARGET|WS_CLOSED/iu.test(output)) return "infra";
|
|
66
|
-
if (/
|
|
66
|
+
if (/fixture|keyring|not seeded|\bsrp\b|password|onboard/iu.test(output)) return "wallet";
|
|
67
67
|
if (/metro|cdp|chrome|bridge|bundle|packager|port\b|econnrefused|not reachable|watcher|dev client|websocket/iu.test(output)) {
|
|
68
68
|
return "infra";
|
|
69
69
|
}
|
|
@@ -101,8 +101,8 @@ function checkHealBounds(target, output, state) {
|
|
|
101
101
|
return {
|
|
102
102
|
code: "WALLET_STATE_REQUIRED",
|
|
103
103
|
exitCode: EXIT.bounded,
|
|
104
|
-
message: "
|
|
105
|
-
userAction: "run mm-harness
|
|
104
|
+
message: "wallet prerequisites need attention; healing never changes wallet state.",
|
|
105
|
+
userAction: /(?:^|\n)\s*Next:\s*([^\r\n]+)/u.exec(output)?.[1] ?? "run mm-harness doctor --json",
|
|
106
106
|
originalError
|
|
107
107
|
};
|
|
108
108
|
}
|
|
@@ -334,6 +334,10 @@ async function runLiveAdapterScript({ platform, action, node, context, prepared
|
|
|
334
334
|
throw new Error(`Live adapter ${script} timed out after ${processTimeoutMs}ms.`);
|
|
335
335
|
}
|
|
336
336
|
if (result.exitCode !== 0) {
|
|
337
|
+
const failureOutput = await readFile(outputPath, "utf8").then(JSON.parse).catch(() => null);
|
|
338
|
+
if (Array.isArray(failureOutput?.artifacts)) {
|
|
339
|
+
for (const artifact of failureOutput.artifacts) context.registerArtifact(artifact);
|
|
340
|
+
}
|
|
337
341
|
throw new Error(`Live adapter ${script} exited ${result.exitCode}: ${result.stderr || result.stdout}`);
|
|
338
342
|
}
|
|
339
343
|
let parsed = null;
|
package/dist/manifest.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { OFFICIAL_RECIPE_ACTIONS } from "@farmslot/protocol";
|
|
4
4
|
import { manifestPath, readJson, importRecipeProtocol } from "./paths.js";
|
|
5
|
-
import { actionSourceIntegrity } from "./recipe-security.js";
|
|
6
5
|
function loadMetaMaskMobileActionManifest() {
|
|
7
6
|
return asActionManifest(readJson(manifestPath("mobile")));
|
|
8
7
|
}
|
|
@@ -26,7 +25,6 @@ async function resolveActionManifest(adapter, overridePath, librarySources, task
|
|
|
26
25
|
);
|
|
27
26
|
const manifest = loadActionManifest(adapter, manifestPath2);
|
|
28
27
|
await validateManifest(manifest);
|
|
29
|
-
const integrity = await actionSourceIntegrity(manifestPath2, "task", implementationRoot);
|
|
30
28
|
return {
|
|
31
29
|
manifest,
|
|
32
30
|
actionSources: await describeManifestSources(manifest, {
|
|
@@ -34,8 +32,7 @@ async function resolveActionManifest(adapter, overridePath, librarySources, task
|
|
|
34
32
|
tier: "task",
|
|
35
33
|
manifestPath: manifestPath2,
|
|
36
34
|
implementationRoot,
|
|
37
|
-
trust: "unknown"
|
|
38
|
-
...integrity
|
|
35
|
+
trust: "unknown"
|
|
39
36
|
})
|
|
40
37
|
};
|
|
41
38
|
}
|
|
@@ -67,12 +64,6 @@ async function resolveActionManifest(adapter, overridePath, librarySources, task
|
|
|
67
64
|
for (const [name, entry] of Object.entries(canonicalActions)) {
|
|
68
65
|
if (official.has(name)) mergedActions[name] = entry;
|
|
69
66
|
}
|
|
70
|
-
const canonicalImplementationRoot = path.join(canonical.source.root, "actions");
|
|
71
|
-
const canonicalIntegrity = await actionSourceIntegrity(
|
|
72
|
-
canonical.file,
|
|
73
|
-
"canonical",
|
|
74
|
-
canonicalImplementationRoot
|
|
75
|
-
);
|
|
76
67
|
const actionSources = /* @__PURE__ */ new Map();
|
|
77
68
|
for (const name of Object.keys(mergedActions)) {
|
|
78
69
|
actionSources.set(name, {
|
|
@@ -96,14 +87,12 @@ async function resolveActionManifest(adapter, overridePath, librarySources, task
|
|
|
96
87
|
const sourceName = isCanonical ? "metamask" : entry.source.name ?? path.basename(entry.source.root);
|
|
97
88
|
const tier = isCanonical ? "canonical" : sourceName === "personal" ? "personal" : "team";
|
|
98
89
|
const implementationRoot = path.join(entry.source.root, "actions");
|
|
99
|
-
const integrity = isCanonical ? canonicalIntegrity : await actionSourceIntegrity(entry.file, tier, implementationRoot);
|
|
100
90
|
const sourceInfo = {
|
|
101
91
|
name: sourceName,
|
|
102
92
|
tier,
|
|
103
93
|
manifestPath: entry.file,
|
|
104
94
|
implementationRoot,
|
|
105
|
-
trust: entry.source.provenance?.trust ?? (isCanonical ? "trusted" : "unknown")
|
|
106
|
-
...integrity
|
|
95
|
+
trust: entry.source.provenance?.trust ?? (isCanonical ? "trusted" : "unknown")
|
|
107
96
|
};
|
|
108
97
|
for (const [name, action] of Object.entries(actionsOf(entry.manifest))) {
|
|
109
98
|
if (official.has(name)) {
|
|
@@ -191,7 +191,8 @@ function validateMetaMaskActionInputs(recipe, adapter, params) {
|
|
|
191
191
|
}
|
|
192
192
|
if (action === "metamask.perps.close_orders") {
|
|
193
193
|
required("side", /^(?:long|short)$/u, `${action} requires the exact order side`);
|
|
194
|
-
|
|
194
|
+
const outputPrice = /^\{\{outputs\.[A-Za-z0-9_.-]+\}\}$/u.test(String(node.limit_price ?? ""));
|
|
195
|
+
if (!outputPrice && (!/^(?:0|[1-9][0-9]*)(?:\.[0-9]{1,6})?$/u.test(String(node.limit_price ?? "")) || !(Number(node.limit_price) > 0))) {
|
|
195
196
|
findings.push({
|
|
196
197
|
severity: "error",
|
|
197
198
|
code: "recipe.invalid_param",
|
|
@@ -200,7 +201,7 @@ function validateMetaMaskActionInputs(recipe, adapter, params) {
|
|
|
200
201
|
});
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
|
-
if (action === "metamask.perps.place_order" && String(node.order_type ?? "market") === "limit" && !/^[1-9][0-9]*(?:\.[0-9]{1,6})?$/u.test(String(node.limit_price ?? ""))) {
|
|
204
|
+
if (action === "metamask.perps.place_order" && String(node.order_type ?? "market") === "limit" && (!/^(?:0|[1-9][0-9]*)(?:\.[0-9]{1,6})?$/u.test(String(node.limit_price ?? "")) || !(Number(node.limit_price) > 0))) {
|
|
204
205
|
findings.push({
|
|
205
206
|
severity: "error",
|
|
206
207
|
code: "recipe.invalid_param",
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -288,7 +288,7 @@ Advanced integrations:
|
|
|
288
288
|
|
|
289
289
|
Example:
|
|
290
290
|
mm-harness run wallet.smoke --describe
|
|
291
|
-
mm-harness run perps.
|
|
291
|
+
mm-harness run perps.release-market-search query=Bitcoin --plan --adapter mobile
|
|
292
292
|
mm-harness run recipe.json market=ETH --adapter extension`
|
|
293
293
|
},
|
|
294
294
|
{
|
|
@@ -613,6 +613,7 @@ Example:
|
|
|
613
613
|
--device <serial> Explicit Android device serial
|
|
614
614
|
--cdp-port <port> Extension CDP port
|
|
615
615
|
--chrome-user-data-dir <dir> Isolated Chrome profile path
|
|
616
|
+
--reset-profile Explicitly clear that profile; default preserves wallet state
|
|
616
617
|
--artifacts-dir <dir> Validation evidence directory
|
|
617
618
|
--start-watch Build through the checkout watcher
|
|
618
619
|
--build-lavamoat Build through the production-like path
|
|
@@ -825,7 +826,8 @@ for (const command of REAL) {
|
|
|
825
826
|
process.exit(await handleUpdate(rawArgv.slice(1)));
|
|
826
827
|
}
|
|
827
828
|
if (command.name === "help") {
|
|
828
|
-
process.
|
|
829
|
+
process.exitCode = handleHelp(rawArgv.slice(1), { packageRoot, harnessVersion: pkgVersion });
|
|
830
|
+
return;
|
|
829
831
|
}
|
|
830
832
|
if (command.name === "tutorial") {
|
|
831
833
|
process.exit(handleTutorial(rawArgv.slice(1), { harnessVersion: pkgVersion }));
|
package/dist/runner.js
CHANGED
|
@@ -20,10 +20,11 @@ import {
|
|
|
20
20
|
bindMetaMaskMobileLifecycleDeadline,
|
|
21
21
|
createMetaMaskMobileLifecycleCommandRunner,
|
|
22
22
|
MOBILE_LIFECYCLE_DEADLINE_FIELD,
|
|
23
|
+
MOBILE_LIFECYCLE_PREPARED,
|
|
23
24
|
resolveMetaMaskMobileLifecycleTarget
|
|
24
25
|
} from "./app-lifecycle.js";
|
|
25
26
|
import { loadMetaMaskExtensionActionManifest, loadMetaMaskMobileActionManifest } from "./manifest.js";
|
|
26
|
-
import { metaMaskActionExecutionCapabilities } from "./recipe-security.js";
|
|
27
|
+
import { actionSourceIntegrity, metaMaskActionExecutionCapabilities } from "./recipe-security.js";
|
|
27
28
|
import {
|
|
28
29
|
prepareLiveAdapterScript
|
|
29
30
|
} from "./live-adapter-contract.js";
|
|
@@ -210,7 +211,7 @@ function withTrustedMutationContext(adapters, base, externalMutationActions, ada
|
|
|
210
211
|
...entry,
|
|
211
212
|
async execute(node, context) {
|
|
212
213
|
const identityProof = entry.action === identityProofAction;
|
|
213
|
-
const invalidatesIdentity = identityInvalidatingActions.has(entry.action) || entry.action.startsWith("ui.") && !identityPreservingUiActions.has(entry.action);
|
|
214
|
+
const invalidatesIdentity = identityInvalidatingActions.has(entry.action) || base?.proofLane !== "source-dev" && entry.action.startsWith("ui.") && !identityPreservingUiActions.has(entry.action);
|
|
214
215
|
if (identityProof || invalidatesIdentity) selectedIdentitySha256 = void 0;
|
|
215
216
|
const externalMutation = externalMutationActions.has(entry.action);
|
|
216
217
|
if (externalMutation && !base) {
|
|
@@ -272,6 +273,15 @@ async function prepareLiveAdapterImplementations(platform, actions, sources, tru
|
|
|
272
273
|
source.digest = executable.sourceDigest;
|
|
273
274
|
source.resolveDigest = void 0;
|
|
274
275
|
}));
|
|
276
|
+
for (const source of sources.values()) {
|
|
277
|
+
if (source.tier === "official" || source.digest) continue;
|
|
278
|
+
const integrity = await actionSourceIntegrity(source.manifestPath, source.tier, source.implementationRoot);
|
|
279
|
+
for (const candidate of sources.values()) {
|
|
280
|
+
if (!candidate.digest && candidate.manifestPath === source.manifestPath && candidate.implementationRoot === source.implementationRoot && candidate.tier === source.tier) {
|
|
281
|
+
Object.assign(candidate, integrity);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
275
285
|
return prepared;
|
|
276
286
|
}
|
|
277
287
|
function adapterSecurity(action, sources, trustTaskActions = false) {
|
|
@@ -405,7 +415,8 @@ function mobileSourceAwareLifecycleAdapters(adapter, lifecycle, readinessProbe =
|
|
|
405
415
|
command: "foreground",
|
|
406
416
|
settle_ms: 0,
|
|
407
417
|
timeout_ms: foregroundTimeoutMs,
|
|
408
|
-
[MOBILE_LIFECYCLE_DEADLINE_FIELD]: readinessDeadline
|
|
418
|
+
[MOBILE_LIFECYCLE_DEADLINE_FIELD]: readinessDeadline,
|
|
419
|
+
[MOBILE_LIFECYCLE_PREPARED]: true
|
|
409
420
|
},
|
|
410
421
|
context
|
|
411
422
|
);
|