@deeeed/metamask-harness 0.79.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/README.md +1 -1
- package/adapters/extension/ensure-browser.sh +2 -2
- package/adapters/extension/inject.mjs +0 -1
- package/adapters/extension/launch-browser.cjs +39 -613
- package/adapters/extension/readiness.mjs +2 -5
- package/adapters/extension/reattach.sh +2 -2
- package/adapters/extension/stamp-runtime-title.cjs +1 -1
- package/adapters/manifest.json +16 -64
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
- package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
- package/adapters/mobile/inject.sh +16 -0
- package/adapters/mobile/launch-console-forwarder.cjs +1 -2
- package/adapters/mobile/open-device.sh +2 -2
- package/adapters/mobile/reload-app.mjs +4 -4
- package/adapters/mobile/reset-app-data.sh +1 -1
- package/adapters/mobile/start-console-forwarder.sh +3 -2
- package/adapters/mobile/start-metro.sh +17 -12
- package/adapters/mobile/wait-for-bridge.cjs +2 -2
- package/adapters/shared/harness-source-fingerprint.mjs +13 -8
- package/adapters/shared/open-debug.mjs +2 -2
- package/adapters/shared/sync-wallet-fixture.sh +8 -5
- package/adapters/terminal/cleanup.mjs +2 -1
- package/adapters/terminal/inject.mjs +2 -1
- package/adapters/terminal/launch.mjs +17 -13
- package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
- package/adapters/terminal/lib/readiness.mjs +20 -12
- package/adapters/terminal/policy.mjs +35 -0
- package/adapters/terminal/verify.mjs +10 -3
- package/adapters/terminal/wallet-host.mjs +43 -225
- package/bin/mm-harness +9 -28
- package/dist/adapters/checkout-files.js +16 -0
- package/dist/adapters/console-capture.js +14 -1
- package/dist/adapters/core/actions.js +8 -4
- package/dist/adapters/core/overlay.js +1 -1
- package/dist/adapters/core/readiness.js +28 -1
- package/dist/adapters/core/run.js +2 -2
- package/dist/adapters/core/surface.js +16 -1
- package/dist/adapters/extension/actions.js +6 -2
- package/dist/adapters/extension/browser-pid.js +56 -0
- package/dist/adapters/extension/console-capture.js +1 -1
- package/dist/adapters/extension/doctor-checks.js +1 -1
- package/dist/adapters/extension/ensure-ready.js +5 -4
- package/dist/adapters/extension/extension-id.js +1 -1
- package/dist/adapters/extension/feature-flags.js +1 -1
- package/dist/adapters/extension/fixtures.js +1 -2
- package/dist/adapters/extension/launch.js +4 -5
- package/dist/adapters/extension/launched-browser.js +1 -2
- package/dist/adapters/extension/network-observer.js +14 -1
- package/dist/adapters/extension/overlay.js +1 -1
- package/dist/adapters/extension/product-config.js +1 -1
- package/dist/adapters/extension/readiness.js +1 -1
- package/dist/adapters/extension/reload.js +1 -1
- package/dist/adapters/extension/run.js +135 -14
- package/dist/adapters/extension/runtime-decision.js +2 -1
- package/dist/adapters/extension/runtime.js +9 -5
- package/dist/adapters/extension/surface.js +15 -2
- package/dist/adapters/extension/verify.js +2 -2
- package/dist/adapters/harness-freshness.js +2 -1
- package/dist/adapters/mobile/actions.js +4 -1
- package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
- package/dist/adapters/mobile/app-identity.js +10 -0
- package/dist/adapters/mobile/doctor-checks.js +1 -1
- package/dist/adapters/mobile/fixtures.js +1 -1
- package/dist/adapters/mobile/launch.js +27 -28
- package/dist/adapters/mobile/lifecycle.js +1 -1
- package/dist/adapters/mobile/metro-env.js +9 -59
- package/dist/adapters/mobile/network-observer.js +68 -0
- package/dist/adapters/mobile/performance-observer.js +2 -4
- package/dist/adapters/mobile/prepare-target.js +1 -1
- package/dist/adapters/mobile/prepare.js +3 -4
- package/dist/adapters/mobile/provision.js +1 -1
- package/dist/adapters/mobile/readiness.js +1 -1
- package/dist/adapters/mobile/recording-target.js +15 -0
- package/dist/adapters/mobile/release-artifact-state.js +1 -1
- package/dist/adapters/mobile/reload.js +1 -1
- package/dist/adapters/mobile/run.js +39 -15
- package/dist/adapters/mobile/runtime-decision.js +2 -4
- package/dist/adapters/mobile/source-freshness.js +22 -126
- package/dist/adapters/mobile/surface.js +26 -3
- package/dist/adapters/mobile/ui.js +16 -5
- package/dist/adapters/mobile/verify.js +2 -3
- package/dist/adapters/performance/cdp-trace.js +1 -1
- package/dist/adapters/performance/device-platform.js +1 -1
- package/dist/adapters/perps-order-inputs.js +36 -0
- package/dist/adapters/proof-failure.js +11 -0
- package/dist/adapters/slot-ports.js +1 -1
- package/dist/adapters/surface.js +1 -0
- package/dist/adapters/terminal/actions.js +4 -1
- package/dist/adapters/terminal/console-capture.js +1 -1
- package/dist/adapters/terminal/doctor-checks.js +2 -0
- package/dist/adapters/terminal/launch.js +4 -1
- package/dist/adapters/terminal/overlay.js +8 -1
- package/dist/adapters/terminal/run.js +33 -5
- package/dist/adapters/terminal/surface.js +9 -1
- package/dist/adapters.js +27 -7
- package/dist/advisor-policy.js +1 -1
- package/dist/checkout-view.js +1 -2
- package/dist/cli.js +59 -21
- package/dist/command-contract.js +1 -26
- package/dist/commands/check.js +33 -10
- package/dist/commands/checklist.js +1 -1
- package/dist/commands/completion-candidates.js +6 -14
- package/dist/commands/config.js +2 -2
- package/dist/commands/core-readiness.js +12 -7
- package/dist/commands/debug.js +3 -3
- package/dist/commands/device-target.js +5 -5
- package/dist/commands/doctor.js +2 -14
- package/dist/commands/domain.js +2 -2
- package/dist/commands/ensure-ready.js +1 -7
- package/dist/commands/fixtures.js +5 -6
- package/dist/commands/help.js +3 -4
- package/dist/commands/launch/device.js +10 -17
- package/dist/commands/launch/extension.js +2 -8
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/commands/logs.js +3 -3
- package/dist/commands/mobile-device-view.js +4 -2
- package/dist/commands/overlay.js +110 -0
- package/dist/commands/pin.js +1 -1
- package/dist/commands/pr-body.js +2 -2
- package/dist/commands/prepare.js +4 -9
- package/dist/commands/provision.js +3 -12
- package/dist/commands/recipe-advice.js +1 -2
- package/dist/commands/recipe-quality.js +2 -3
- package/dist/commands/resolve-extension.js +1 -7
- package/dist/commands/review.js +4 -4
- package/dist/commands/runtime-decision.js +1 -6
- package/dist/commands/runtime-health.js +1 -7
- package/dist/commands/runtime-launch.js +1 -13
- package/dist/commands/self-test.js +5 -8
- package/dist/commands/status-watch.js +1 -2
- package/dist/commands/status.js +2 -5
- package/dist/commands/task-init.js +2 -3
- package/dist/commands/tutorial.js +2 -2
- package/dist/commands/update.js +1 -1
- package/dist/completions-cache.js +1 -1
- package/dist/console-classifier.js +19 -0
- package/dist/doctor.js +2 -3
- package/dist/engine.js +57 -0
- package/dist/funding-execution-context.js +96 -1
- package/dist/host.js +32 -0
- package/dist/index.js +1 -6
- package/dist/live-tsx.js +49 -0
- package/dist/mm-harness-cli.js +4 -6
- package/dist/paths.js +34 -64
- package/dist/performance-observation.js +3 -5
- package/dist/progress.js +1 -1
- package/dist/recipe-assistance-context.js +6 -9
- package/dist/recipe-security.js +2 -59
- package/dist/review/knowledge.js +2 -2
- package/dist/runner.js +7 -4
- package/dist/runtime-context.js +1 -1
- package/dist/task-view.js +2 -3
- package/docs/CONTRIBUTING.md +5 -3
- package/docs/RECIPES.md +31 -3
- package/docs/cli-contract.md +2 -2
- package/docs/feature-guard.md +90 -0
- package/library/README.md +3 -1
- package/library/actions/core/perps/_controller.mjs +153 -134
- package/library/actions/core/perps/_rewards.mjs +186 -0
- package/library/actions/core/perps/close_positions.mjs +1 -1
- package/library/actions/core/perps/place_order.mjs +167 -45
- package/library/actions/core/perps/quote_fees.mjs +61 -0
- package/library/actions/core/wallet/list_accounts.mjs +14 -57
- package/library/actions/extension/feature_flags/state.mjs +1 -1
- package/library/actions/extension/perps/close_visible_position.mjs +5 -0
- package/library/actions/extension/perps/edit_margin.mjs +4 -0
- package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
- package/library/actions/extension/perps/perps.mjs +320 -3
- package/library/actions/extension/perps/place_order.mjs +3 -1
- package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
- package/library/actions/extension/platform/cdp.mjs +73 -11
- package/library/actions/harness-exports.mjs +1 -6
- package/library/actions/mobile/app/network-control.mjs +6 -8
- package/library/actions/mobile/perps/perps.mjs +216 -11
- package/library/actions/mobile/perps/place_order.mjs +3 -1
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +8 -8
- package/library/actions/mobile/platform/native-session-name.mjs +21 -1
- package/library/actions/mobile/platform/native-session.mjs +3 -4
- package/library/actions/mobile/platform/observe-ui.mjs +12 -6
- package/library/actions/mobile/ui/native-navigation.mjs +4 -3
- package/library/actions/mobile/wallet/native-ui.mjs +3 -2
- package/library/actions/shared/analytics/_adapter.mjs +1 -1
- package/library/actions/shared/console-findings.mjs +1 -1
- package/library/actions/shared/perps/order-policy.mjs +177 -0
- package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
- package/library/actions/terminal/app/launch.mjs +3 -2
- package/library/actions/terminal/platform/hud.mjs +2 -1
- package/library/actions/terminal/platform/page.mjs +2 -1
- package/library/actions/terminal/platform/runtime.mjs +2 -1
- package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
- package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
- package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
- package/library/manifests/core.action-manifest.json +87 -13
- package/library/manifests/extension.action-manifest.json +4 -0
- package/library/manifests/mobile.action-manifest.json +12 -0
- package/package.json +8 -9
- package/site/assets/help-recipes.json +1 -0
- package/adapters/extension/lib/slot-title.cjs +0 -292
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
- package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
- package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
- package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
- package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
- package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
- package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
- package/adapters/mobile/coalesce-metro-log.cjs +0 -24
- package/adapters/mobile/launch-metro.cjs +0 -77
- package/adapters/mobile/metro-config.cjs +0 -98
- package/adapters/mobile/metro-log-generation.cjs +0 -106
- package/adapters/terminal/lib/origin.mjs +0 -30
- package/adapters/terminal/lib/page-script.mjs +0 -246
- package/adapters/terminal/lib/strict-wallet.mjs +0 -138
- package/dist/adapters/mobile/frame-metrics.js +0 -118
- package/dist/adapters/mobile/video-recorder.js +0 -289
- package/dist/artifact-files.js +0 -92
- package/dist/checkout-lock.js +0 -149
- package/dist/cli-color.js +0 -98
- package/dist/command-journal.js +0 -308
- package/dist/commands/call.js +0 -685
- package/dist/commands/last.js +0 -60
- package/dist/commands/launch/index.js +0 -109
- package/dist/commands/list-executables.js +0 -247
- package/dist/commands/manifest.js +0 -488
- package/dist/commands/parse-args.js +0 -279
- package/dist/commands/reload.js +0 -41
- package/dist/commands/run-engine.js +0 -1340
- package/dist/commands/run-report.js +0 -241
- package/dist/commands/run.js +0 -751
- package/dist/commands/shared.js +0 -323
- package/dist/commands/stop.js +0 -60
- package/dist/devices.js +0 -100
- package/dist/discovery.js +0 -15
- package/dist/execution-provenance.js +0 -332
- package/dist/harness.js +0 -451
- package/dist/heal-bounds.js +0 -137
- package/dist/json-stream.js +0 -60
- package/dist/leaf-invoke.js +0 -19
- package/dist/library-provenance.js +0 -33
- package/dist/live-adapter-contract.js +0 -380
- package/dist/metamask-action-validation.js +0 -68
- package/dist/network-observation.js +0 -290
- package/dist/recording-target.js +0 -120
- package/dist/run-diagnostics.js +0 -364
- package/dist/run-recording.js +0 -481
- package/dist/runtime-proof.js +0 -90
- package/library/actions/mobile/platform/tool-paths.mjs +0 -123
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// MetaMask Mobile recovery lines for the bridge error codes that
|
|
4
|
+
// @farmslot/adapter-rn classifies; cdp-bridge.cjs prints one after a coded failure.
|
|
5
|
+
//
|
|
6
|
+
// Every coded failure teaches the escape for the caller's ACTUAL situation — a
|
|
7
|
+
// down/transitioning target is recoverable, so the Next: line names the action
|
|
8
|
+
// that brings it back rather than leaving the operator to guess.
|
|
9
|
+
const TEACHING_BY_ERROR_CODE = {
|
|
10
|
+
NO_TARGET:
|
|
11
|
+
'Next: bring the app to the foreground with `mm-harness launch <platform>` so a debug target is available.',
|
|
12
|
+
CDP_TIMEOUT:
|
|
13
|
+
'Next: the app is reloading or busy — retry, or `mm-harness launch <platform>` to foreground it.',
|
|
14
|
+
WS_CLOSED:
|
|
15
|
+
'Next: the Hermes debug page closed (app reload/backgrounded) — retry once the app settles.',
|
|
16
|
+
METRO_UNREACHABLE:
|
|
17
|
+
'Next: start Metro with `mm-harness start-metro` (if it is already up, check the slot WATCHER_PORT).',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = { TEACHING_BY_ERROR_CODE };
|
|
@@ -77,6 +77,22 @@ rsync -a --delete "$RUNNER_DIR/library/recipes/" "$HARNESS_DIR/runner/recipes/"
|
|
|
77
77
|
rm -rf "$HARNESS_DIR/runner/flows"
|
|
78
78
|
rm -f "$HARNESS_DIR/runner/library.json"
|
|
79
79
|
cp -R "$RUNNER_DIR/adapters/mobile/bridge-runtime" "$HARNESS_DIR/bridge-runtime"
|
|
80
|
+
# cdp-bridge.cjs runs from the target checkout, which cannot resolve the runner's
|
|
81
|
+
# packages; vendor the bridge libraries it requires next to it.
|
|
82
|
+
node - "$RUNNER_DIR" "$HARNESS_DIR/bridge-runtime/node_modules/@farmslot" <<'NODE' || exit 1
|
|
83
|
+
const fs = require('node:fs');
|
|
84
|
+
const path = require('node:path');
|
|
85
|
+
const { createRequire } = require('node:module');
|
|
86
|
+
const [runnerDir, dest] = process.argv.slice(2);
|
|
87
|
+
const requireFromRunner = createRequire(path.join(runnerDir, 'package.json'));
|
|
88
|
+
for (const [name, dirs] of [['adapter-rn', ['bridge-runtime']], ['recipe-runner', ['cdp-broker']]]) {
|
|
89
|
+
const packageJson = requireFromRunner.resolve(`@farmslot/${name}/package.json`);
|
|
90
|
+
const target = path.join(dest, name);
|
|
91
|
+
fs.mkdirSync(target, { recursive: true });
|
|
92
|
+
fs.copyFileSync(packageJson, path.join(target, 'package.json'));
|
|
93
|
+
for (const dir of dirs) fs.cpSync(path.join(path.dirname(packageJson), dir), path.join(target, dir), { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
NODE
|
|
80
96
|
chmod -R u+rwX,go+rX "$HARNESS_DIR"
|
|
81
97
|
|
|
82
98
|
add_git_exclude() {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const fs = require('node:fs');
|
|
5
|
-
const path = require('node:path');
|
|
6
5
|
const { spawn } = require('node:child_process');
|
|
7
6
|
|
|
8
7
|
const args = { port: '', out: '', err: '' };
|
|
@@ -25,7 +24,7 @@ if (!args.port || !args.out || !args.err) {
|
|
|
25
24
|
process.exit(2);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
const forwarder =
|
|
27
|
+
const forwarder = require.resolve('@farmslot/adapter-rn/bridge-runtime/console-forwarder.cjs');
|
|
29
28
|
const errFd = fs.openSync(args.err, 'a');
|
|
30
29
|
try {
|
|
31
30
|
const child = spawn(process.execPath, [forwarder, '--port', args.port, '--out', args.out], {
|
|
@@ -28,7 +28,7 @@ PLATFORM="ios"
|
|
|
28
28
|
TARGET="$PWD"
|
|
29
29
|
SIMULATOR="${SIM_UDID:-${IOS_SIMULATOR:-booted}}"
|
|
30
30
|
ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
|
|
31
|
-
ADB_BIN="${
|
|
31
|
+
ADB_BIN="${RECIPE_RN_ADB_PATH:-}"
|
|
32
32
|
PREFLIGHT_MODE="${MOBILE_PREFLIGHT_MODE:-fast}"
|
|
33
33
|
PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
|
|
34
34
|
UI_ONLY=false
|
|
@@ -67,7 +67,7 @@ if [ "$PLATFORM" = "android" ]; then
|
|
|
67
67
|
printf 'Next: brew install android-platform-tools\n' >&2
|
|
68
68
|
exit 1
|
|
69
69
|
fi
|
|
70
|
-
export
|
|
70
|
+
export RECIPE_RN_ADB_PATH="$ADB_BIN"
|
|
71
71
|
fi
|
|
72
72
|
|
|
73
73
|
TARGET="$(cd "$TARGET" && pwd)"
|
|
@@ -4,13 +4,13 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
7
|
-
import { resolveMobileToolPath } from '
|
|
7
|
+
import { resolveMobileToolPath } from '@farmslot/adapter-rn';
|
|
8
8
|
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
10
|
const WebSocket = require('ws');
|
|
11
|
-
const { discoverTarget, rankRuntimeCandidates } = require('
|
|
12
|
-
const { brokerSocketPath, createBrokerClient, deviceIdFromUrl } = require('
|
|
13
|
-
const { createWSClient } = require('
|
|
11
|
+
const { discoverTarget, rankRuntimeCandidates } = require('@farmslot/adapter-rn/bridge-runtime/lib/target-discovery.cjs');
|
|
12
|
+
const { brokerSocketPath, createBrokerClient, deviceIdFromUrl } = require('@farmslot/recipe-runner/cdp-broker');
|
|
13
|
+
const { createWSClient } = require('@farmslot/adapter-rn/bridge-runtime/lib/ws-client.cjs');
|
|
14
14
|
|
|
15
15
|
async function observeRuntimeReset(port, target) {
|
|
16
16
|
const socketPath = brokerSocketPath(
|
|
@@ -6,7 +6,7 @@ PLATFORM="${PLATFORM:-ios}"
|
|
|
6
6
|
TARGET="$PWD"
|
|
7
7
|
SIMULATOR="${SIM_UDID:-${IOS_SIMULATOR:-booted}}"
|
|
8
8
|
ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
|
|
9
|
-
ADB_BIN="${
|
|
9
|
+
ADB_BIN="${RECIPE_RN_ADB_PATH:-}"
|
|
10
10
|
MM_RESET_TEMP_ROOT=""
|
|
11
11
|
MM_RESET_STAGING_DIR=""
|
|
12
12
|
|
|
@@ -23,13 +23,14 @@ done
|
|
|
23
23
|
TARGET="$(cd "$TARGET" && pwd -P)"
|
|
24
24
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
25
25
|
if [ -z "$PORT" ] && command -v node >/dev/null 2>&1; then
|
|
26
|
-
PORT="$(
|
|
26
|
+
PORT="$(cd "$SCRIPT_DIR" && APP_ROOT="$TARGET" node -e "process.stdout.write(String(require('@farmslot/adapter-rn/bridge-runtime/lib/config.cjs').resolvePort()))")"
|
|
27
27
|
fi
|
|
28
28
|
PORT="${PORT:-8081}"
|
|
29
29
|
# shellcheck disable=SC1091
|
|
30
30
|
. "$SCRIPT_DIR/../shared/harness-path.sh"
|
|
31
31
|
LOG_DIR="$TARGET/$(recipe_runtime_dir)" || exit 1
|
|
32
|
-
|
|
32
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
33
|
+
FORWARDER="$(cd "$SCRIPT_DIR" && node -p "require.resolve('@farmslot/adapter-rn/bridge-runtime/console-forwarder.cjs')")" || exit 1
|
|
33
34
|
PID_FILE="$LOG_DIR/console-forwarder.pid"
|
|
34
35
|
APP_LOG_FILE="$LOG_DIR/app-console.log"
|
|
35
36
|
|
|
@@ -54,18 +54,23 @@ fi
|
|
|
54
54
|
# only ever inspect or signal a Metro listening on $PORT.
|
|
55
55
|
# shellcheck disable=SC1091
|
|
56
56
|
. "$SCRIPT_DIR/lib/metro-listener.sh"
|
|
57
|
-
|
|
57
|
+
# Metro config wrapper, launcher and log helpers ship in @farmslot/adapter-rn.
|
|
58
|
+
ADAPTER_RN_METRO_DIR="$(cd "$SCRIPT_DIR" && node -p "require('node:path').dirname(require.resolve('@farmslot/adapter-rn/metro/metro-config.cjs'))")" || {
|
|
59
|
+
echo "start-metro: @farmslot/adapter-rn is not installed next to the runner; reinstall the runner." >&2
|
|
60
|
+
exit 1
|
|
61
|
+
}
|
|
62
|
+
HARNESS_METRO_CONFIG="$ADAPTER_RN_METRO_DIR/metro-config.cjs"
|
|
58
63
|
if [ "${EXPO_OVERRIDE_METRO_CONFIG:-}" = "$HARNESS_METRO_CONFIG" ]; then
|
|
59
|
-
PRODUCT_METRO_CONFIG="${
|
|
64
|
+
PRODUCT_METRO_CONFIG="${RECIPE_RN_METRO_BASE_CONFIG:-$TARGET/metro.config.js}"
|
|
60
65
|
else
|
|
61
66
|
PRODUCT_METRO_CONFIG="${EXPO_OVERRIDE_METRO_CONFIG:-$TARGET/metro.config.js}"
|
|
62
67
|
fi
|
|
63
|
-
export
|
|
64
|
-
export
|
|
68
|
+
export RECIPE_RN_METRO_PROJECT_ROOT="$TARGET"
|
|
69
|
+
export RECIPE_RN_METRO_BASE_CONFIG="$PRODUCT_METRO_CONFIG"
|
|
65
70
|
export EXPO_OVERRIDE_METRO_CONFIG="$HARNESS_METRO_CONFIG"
|
|
66
|
-
MOBILE_METRO_REQUIRED_ENV="${MOBILE_METRO_REQUIRED_ENV:-MM_INFURA_PROJECT_ID METAMASK_BUILD_TYPE METAMASK_ENVIRONMENT}
|
|
67
|
-
if [ -n "${
|
|
68
|
-
MOBILE_METRO_REQUIRED_ENV="$MOBILE_METRO_REQUIRED_ENV
|
|
71
|
+
MOBILE_METRO_REQUIRED_ENV="${MOBILE_METRO_REQUIRED_ENV:-MM_INFURA_PROJECT_ID METAMASK_BUILD_TYPE METAMASK_ENVIRONMENT} RECIPE_RN_METRO_PROJECT_ROOT RECIPE_RN_METRO_BASE_CONFIG EXPO_OVERRIDE_METRO_CONFIG"
|
|
72
|
+
if [ -n "${RECIPE_RN_METRO_ENV_FINGERPRINT:-}" ]; then
|
|
73
|
+
MOBILE_METRO_REQUIRED_ENV="$MOBILE_METRO_REQUIRED_ENV RECIPE_RN_METRO_ENV_FINGERPRINT"
|
|
69
74
|
fi
|
|
70
75
|
|
|
71
76
|
# RECIPE_RUNTIME_DIR (relative, validated) makes runtime state per-run so jobs
|
|
@@ -75,8 +80,8 @@ mkdir -p "$LOG_DIR"
|
|
|
75
80
|
LOG_FILE="$LOG_DIR/metro.log"
|
|
76
81
|
PID_FILE="$LOG_DIR/metro.pid"
|
|
77
82
|
METRO_BUILD_ENV_FILE="$LOG_DIR/metro-build-env.sh"
|
|
78
|
-
METRO_LOG_GENERATION="$
|
|
79
|
-
METRO_LOG_COALESCER="$
|
|
83
|
+
METRO_LOG_GENERATION="$ADAPTER_RN_METRO_DIR/metro-log-generation.cjs"
|
|
84
|
+
METRO_LOG_COALESCER="$ADAPTER_RN_METRO_DIR/coalesce-metro-log.cjs"
|
|
80
85
|
|
|
81
86
|
# --- helpers ------------------------------------------------------------------
|
|
82
87
|
|
|
@@ -106,8 +111,8 @@ write_metro_build_env() {
|
|
|
106
111
|
done
|
|
107
112
|
)" || { rm -f "$temporary"; return 1; }
|
|
108
113
|
eval "$product_values"
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
required_RECIPE_RN_METRO_PROJECT_ROOT="$RECIPE_RN_METRO_PROJECT_ROOT"
|
|
115
|
+
required_RECIPE_RN_METRO_BASE_CONFIG="$RECIPE_RN_METRO_BASE_CONFIG"
|
|
111
116
|
required_EXPO_OVERRIDE_METRO_CONFIG="$EXPO_OVERRIDE_METRO_CONFIG"
|
|
112
117
|
[ "$CLEAR" = true ] && clear_value=1
|
|
113
118
|
{
|
|
@@ -322,7 +327,7 @@ else
|
|
|
322
327
|
)
|
|
323
328
|
[ -z "$METRO_WORKERS" ] || launcher_args+=(--workers "$METRO_WORKERS")
|
|
324
329
|
[ "$CLEAR" = true ] && launcher_args+=(--clear)
|
|
325
|
-
node "$
|
|
330
|
+
node "$ADAPTER_RN_METRO_DIR/launch-metro.cjs" "${launcher_args[@]}"
|
|
326
331
|
) || exit 1
|
|
327
332
|
STARTED_METRO_PID="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
328
333
|
|
|
@@ -14,10 +14,10 @@ const {
|
|
|
14
14
|
describeRequested,
|
|
15
15
|
describeTargets,
|
|
16
16
|
hasMatchingRoute,
|
|
17
|
-
} = require('
|
|
17
|
+
} = require('@farmslot/adapter-rn/bridge-runtime/lib/match-bridge-target.cjs');
|
|
18
18
|
const {
|
|
19
19
|
rankRuntimeCandidates,
|
|
20
|
-
} = require('
|
|
20
|
+
} = require('@farmslot/adapter-rn/bridge-runtime/lib/target-discovery.cjs');
|
|
21
21
|
|
|
22
22
|
const DEFAULT_MAX_POLLS = 90;
|
|
23
23
|
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// the next launch refreshes every slot from it.
|
|
9
9
|
//
|
|
10
10
|
// Source files count by size + mtime, so it is cheap enough to run on every launch;
|
|
11
|
-
// the only
|
|
11
|
+
// the only files read are the vendored packages' package.json, for their versions. No
|
|
12
12
|
// Date.now()/random is used (both would break determinism).
|
|
13
13
|
import fs from 'node:fs';
|
|
14
14
|
import path from 'node:path';
|
|
@@ -25,17 +25,22 @@ export function harnessSourceFingerprint(runnerDir, adapter = 'extension') {
|
|
|
25
25
|
for (const rel of sourceDirs) {
|
|
26
26
|
collect(path.join(runnerDir, rel), runnerDir, entries);
|
|
27
27
|
}
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
// The injectors copy package files into the overlay (Extension: @farmslot/adapter-web's
|
|
29
|
+
// browser helpers; Mobile: the bridge libraries cdp-bridge.cjs requires), so those
|
|
30
|
+
// sources are overlay inputs too; label them by package so the install location
|
|
31
|
+
// does not count.
|
|
32
|
+
const vendored = adapter === 'extension'
|
|
33
|
+
? [['@farmslot/adapter-web', ['src']]]
|
|
34
|
+
: [['@farmslot/adapter-rn', ['bridge-runtime']], ['@farmslot/recipe-runner', ['cdp-broker']]];
|
|
35
|
+
for (const [name, dirs] of vendored) {
|
|
36
|
+
const packageJson = createRequire(path.join(runnerDir, 'package.json')).resolve(`${name}/package.json`);
|
|
32
37
|
const packageRoot = path.dirname(packageJson);
|
|
33
38
|
const packageEntries = [];
|
|
34
|
-
collect(path.join(packageRoot,
|
|
35
|
-
entries.push(...packageEntries.map((entry) =>
|
|
39
|
+
for (const dir of dirs) collect(path.join(packageRoot, dir), packageRoot, packageEntries);
|
|
40
|
+
entries.push(...packageEntries.map((entry) => `${name}/${entry}`));
|
|
36
41
|
// npm tarballs carry fixed mtimes, so an upgrade can keep every size and mtime:
|
|
37
42
|
// the version marks it.
|
|
38
|
-
entries.push(
|
|
43
|
+
entries.push(`${name}@${JSON.parse(fs.readFileSync(packageJson, 'utf8')).version}`);
|
|
39
44
|
}
|
|
40
45
|
entries.sort();
|
|
41
46
|
return crypto.createHash('sha256').update(entries.join('\n')).digest('hex');
|
|
@@ -15,10 +15,10 @@ import path from 'node:path';
|
|
|
15
15
|
const require = createRequire(import.meta.url);
|
|
16
16
|
const {
|
|
17
17
|
discoverTarget,
|
|
18
|
-
} = require('
|
|
18
|
+
} = require('@farmslot/adapter-rn/bridge-runtime/lib/target-discovery.cjs');
|
|
19
19
|
const {
|
|
20
20
|
deviceIdFromUrl,
|
|
21
|
-
} = require('
|
|
21
|
+
} = require('@farmslot/recipe-runner/cdp-broker');
|
|
22
22
|
|
|
23
23
|
function usage() {
|
|
24
24
|
console.error(`Usage:
|
|
@@ -60,12 +60,15 @@ fi
|
|
|
60
60
|
|
|
61
61
|
# Farmslot slot sync (requires farmslot root and sync-fixtures.sh).
|
|
62
62
|
if [ -n "$SLOT_ID" ]; then
|
|
63
|
+
# An explicit FARMSLOT_ROOT wins; otherwise the operator checkout's farm script.
|
|
63
64
|
FARMSLOT_ROOT="${FARMSLOT_ROOT:-}"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
if [ -z "$FARMSLOT_ROOT" ]; then
|
|
66
|
+
for candidate in "$HOME/dev/farmslot" "$HOME/farmslot"; do
|
|
67
|
+
[ -f "$candidate/scripts/sync-fixtures.sh" ] || continue
|
|
68
|
+
FARMSLOT_ROOT="$candidate"
|
|
69
|
+
break
|
|
70
|
+
done
|
|
71
|
+
fi
|
|
69
72
|
if [ -n "$FARMSLOT_ROOT" ] && [ -f "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" ]; then
|
|
70
73
|
if bash "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" --slot "$SLOT_ID" 2>/dev/null; then
|
|
71
74
|
[ -f "$CANONICAL" ] && exit 0
|
|
@@ -11,7 +11,8 @@ import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
|
|
14
|
-
import { recipeHarnessPath
|
|
14
|
+
import { recipeHarnessPath } from '@farmslot/recipe-cli/harness';
|
|
15
|
+
import { walletFixturePath } from '../../library/actions/harness-exports.mjs';
|
|
15
16
|
import { terminalRuntimeDir } from '../../library/actions/terminal/platform/runtime.mjs';
|
|
16
17
|
import { stopConsoleCollector, stopTerminalBrowser } from './lib/processes.mjs';
|
|
17
18
|
|
|
@@ -13,7 +13,8 @@ import { copyFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFile
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
|
|
16
|
-
import { recipeHarnessPath, recipeRuntimeDir
|
|
16
|
+
import { recipeHarnessPath, recipeRuntimeDir } from '@farmslot/recipe-cli/harness';
|
|
17
|
+
import { walletFixturePath } from '../../library/actions/harness-exports.mjs';
|
|
17
18
|
import { terminalRuntimeDir } from '../../library/actions/terminal/platform/runtime.mjs';
|
|
18
19
|
import { isTerminalCheckout } from './lib/readiness.mjs';
|
|
19
20
|
|
|
@@ -29,7 +29,7 @@ import { createRequire } from 'node:module';
|
|
|
29
29
|
import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
30
30
|
import os from 'node:os';
|
|
31
31
|
import path from 'node:path';
|
|
32
|
-
import { fileURLToPath } from 'node:url';
|
|
32
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
33
33
|
|
|
34
34
|
import {
|
|
35
35
|
fixtureEntry,
|
|
@@ -42,7 +42,7 @@ import { CdpClient, browserWebSocketUrl } from './lib/cdp-client.mjs';
|
|
|
42
42
|
import { clearHudState } from './lib/hud.mjs';
|
|
43
43
|
import { browserDisplayArgs, resolveHeadless, resolveSlowMo, windowPlacement, writeProfileZoom } from './lib/display.mjs';
|
|
44
44
|
import { cdpListenerPids, ownedBrowserPids, ownerMarker, ownsProcess, pidAlive, stopTerminalBrowser, terminate } from './lib/processes.mjs';
|
|
45
|
-
import { hostResolverRules
|
|
45
|
+
import { hostResolverRules } from './lib/mainnet-guard.mjs';
|
|
46
46
|
import { assertLaunchNetwork, devServerTestnetDiagnostic } from './lib/readiness.mjs';
|
|
47
47
|
|
|
48
48
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -51,7 +51,7 @@ const HARNESS_ROOT = path.resolve(SCRIPT_DIR, '../..');
|
|
|
51
51
|
const FIXTURE_STATE_SCRIPT = path.join(HARNESS_ROOT, 'adapters/extension/wallet-fixture-state.cjs');
|
|
52
52
|
|
|
53
53
|
function usage() {
|
|
54
|
-
return 'Usage: launch.mjs --target <checkout> --cdp-port <port> --app-port <port> [--signer extension|injected] [--account <name>] [--fresh-profile] [--headless | --headful] [--slow-mo <ms>] [--network testnet|mainnet --mainnet-confirmation REAL_FUNDS] [--start-chain <id>] [--json]';
|
|
54
|
+
return 'Usage: launch.mjs --target <checkout> --cdp-port <port> --app-port <port> --policy-module <venue policy module> [--signer extension|injected] [--account <name>] [--fresh-profile] [--headless | --headful] [--slow-mo <ms>] [--network testnet|mainnet --mainnet-confirmation REAL_FUNDS] [--start-chain <id>] [--json]';
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function parseLaunchArgs(argv) {
|
|
@@ -67,7 +67,7 @@ export function parseLaunchArgs(argv) {
|
|
|
67
67
|
args[flag.slice(2)] = argv[index + 1];
|
|
68
68
|
index += 1;
|
|
69
69
|
}
|
|
70
|
-
for (const key of ['target', 'cdp-port', 'app-port']) {
|
|
70
|
+
for (const key of ['target', 'cdp-port', 'app-port', 'policy-module']) {
|
|
71
71
|
if (!args[key]) throw Object.assign(new Error(`Missing --${key}\n${usage()}`), { exitCode: 2 });
|
|
72
72
|
}
|
|
73
73
|
args.signer = resolveSigner(args.signer);
|
|
@@ -344,7 +344,9 @@ function writePrivateFile(file, text) {
|
|
|
344
344
|
// which waits for a cold dev-server compile.
|
|
345
345
|
export const LAUNCH_TIMEOUTS = Object.freeze({ browserStartMs: 90000, hostReadyMs: 180000 });
|
|
346
346
|
|
|
347
|
-
|
|
347
|
+
// `policy`: the venue policy (adapters/terminal/policy.mjs).
|
|
348
|
+
export async function launchTerminalBrowser(args, env = process.env, { timeouts = LAUNCH_TIMEOUTS, policy } = {}) {
|
|
349
|
+
if (!policy?.module) throw new Error('launchTerminalBrowser needs the venue policy (adapters/terminal/policy.mjs).');
|
|
348
350
|
const target = path.resolve(args.target);
|
|
349
351
|
const cdpPort = Number(args['cdp-port']);
|
|
350
352
|
const appPort = Number(args['app-port']);
|
|
@@ -353,12 +355,12 @@ export async function launchTerminalBrowser(args, env = process.env, { timeouts
|
|
|
353
355
|
// browser: the SDK's mainnet venue hosts are unresolvable for the whole
|
|
354
356
|
// browser, and the wallet host blocks, logs and refuses anything mainnet.
|
|
355
357
|
const network = assertLaunchNetwork(target, { network: args.network, mainnetConfirmation: args['mainnet-confirmation'] });
|
|
356
|
-
const venues = network === 'testnet' ? venueHosts(target) : {
|
|
357
|
-
const mainnetHosts = venues.
|
|
358
|
+
const venues = network === 'testnet' ? policy.venueHosts(target) : { blocked: [], served: [] };
|
|
359
|
+
const mainnetHosts = venues.blocked;
|
|
358
360
|
// What the running browser enforces; a browser enforcing anything else is
|
|
359
361
|
// never reused (its resolver rules are fixed at start).
|
|
360
|
-
const enforcementFingerprint = createHash('sha256').update(JSON.stringify({ network, mainnetHosts, testnetHosts: venues.
|
|
361
|
-
const devServer = network === 'testnet' ? devServerTestnetDiagnostic(target, { appPort }) : null;
|
|
362
|
+
const enforcementFingerprint = createHash('sha256').update(JSON.stringify({ network, mainnetHosts, testnetHosts: venues.served })).digest('hex').slice(0, 16);
|
|
363
|
+
const devServer = network === 'testnet' ? devServerTestnetDiagnostic(target, { appPort, variable: policy.testnetVariable }) : null;
|
|
362
364
|
if (devServer && !(devServer.known && devServer.forced)) {
|
|
363
365
|
log(`WARN dev server testnet check: ${devServer.detail} (advisory; the browser enforces testnet itself)`);
|
|
364
366
|
}
|
|
@@ -541,11 +543,12 @@ export async function launchTerminalBrowser(args, env = process.env, { timeouts
|
|
|
541
543
|
'--app-origin', appOrigin,
|
|
542
544
|
'--target', target,
|
|
543
545
|
'--account', args.account,
|
|
544
|
-
'--start-chain', String(args['start-chain'] ??
|
|
546
|
+
'--start-chain', String(args['start-chain'] ?? policy.startChain),
|
|
545
547
|
'--network', network,
|
|
548
|
+
'--policy-module', policy.module,
|
|
546
549
|
ownerMarker('wallet-host', runtime),
|
|
547
550
|
];
|
|
548
|
-
if (network === 'testnet') hostArgs.push('--mainnet-hosts', mainnetHosts.join(','), '--testnet-hosts', venues.
|
|
551
|
+
if (network === 'testnet') hostArgs.push('--mainnet-hosts', mainnetHosts.join(','), '--testnet-hosts', venues.served.join(','));
|
|
549
552
|
if (extension?.id) hostArgs.push('--extension-id', extension.id);
|
|
550
553
|
if (guardFocus) hostArgs.push('--observe-focus', '1');
|
|
551
554
|
if (launchMethod === LAUNCH_SERVICES) hostArgs.push('--window', [windowBounds.x, windowBounds.y, windowBounds.width, windowBounds.height].join(','));
|
|
@@ -568,7 +571,7 @@ export async function launchTerminalBrowser(args, env = process.env, { timeouts
|
|
|
568
571
|
signer,
|
|
569
572
|
network,
|
|
570
573
|
networkEnforcement: network === 'testnet'
|
|
571
|
-
? { mode: 'enforced', mainnetHosts, testnetHosts: venues.
|
|
574
|
+
? { mode: 'enforced', mainnetHosts, testnetHosts: venues.served, fingerprint: enforcementFingerprint, layers: ['host-resolver-rules', 'cdp-fetch-block', 'wallet-refusal', 'served-network-check'] }
|
|
572
575
|
: { mode: 'disabled', reason: 'network=mainnet with mainnet_confirmation=REAL_FUNDS', fingerprint: enforcementFingerprint },
|
|
573
576
|
devServerDiagnostic: devServer,
|
|
574
577
|
consoleOffsets,
|
|
@@ -618,7 +621,8 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
|
|
|
618
621
|
process.exit(0);
|
|
619
622
|
}
|
|
620
623
|
try {
|
|
621
|
-
const
|
|
624
|
+
const { policy } = await import(pathToFileURL(path.resolve(args['policy-module'])).href);
|
|
625
|
+
const state = await launchTerminalBrowser(args, process.env, { policy });
|
|
622
626
|
process.stdout.write(`${JSON.stringify({ status: 'pass', ...state }, null, args.json ? 0 : 2)}\n`);
|
|
623
627
|
} catch (error) {
|
|
624
628
|
const message = String(error?.stderr ?? '').trim() || error.message;
|
|
@@ -175,3 +175,12 @@ export function mainnetTypedDataReason(data) {
|
|
|
175
175
|
if (data?.primaryType === 'Agent' && message.source === 'a') return 'L1 Agent source=a';
|
|
176
176
|
return null;
|
|
177
177
|
}
|
|
178
|
+
|
|
179
|
+
// The wallet refusal for testnet runs, as the @farmslot/adapter-web/dapp page
|
|
180
|
+
// script and wallet-request binding take it: mainnetTypedDataReason runs in
|
|
181
|
+
// the page too, so it stays self-contained.
|
|
182
|
+
export const TESTNET_TYPED_DATA_POLICY = Object.freeze({
|
|
183
|
+
reason: mainnetTypedDataReason,
|
|
184
|
+
kind: 'refused-mainnet',
|
|
185
|
+
message: 'Refused: this test run is held to Hyperliquid testnet and the request signs a mainnet action.',
|
|
186
|
+
});
|
|
@@ -34,8 +34,9 @@ async function httpStatus(url, timeoutMs = 20000) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// Diagnostic only. Whether the slot's dev server looks configured for testnet:
|
|
37
|
-
// the farm starts `next dev` (temp/farmslot/next-dev.pid) with
|
|
38
|
-
//
|
|
37
|
+
// the farm starts `next dev` (temp/farmslot/next-dev.pid) with the policy's
|
|
38
|
+
// testnet variable set to true (the Terminal's
|
|
39
|
+
// NEXT_PUBLIC_HYPERLIQUID_FORCE_TESTNET). Process inspection cannot prove
|
|
39
40
|
// what the browser talks to (macOS reports a process's initial environment,
|
|
40
41
|
// and lsof cannot see other users' sockets), so this only warns; the browser
|
|
41
42
|
// is held to testnet by the network and wallet layers (mainnet-guard.mjs, the
|
|
@@ -45,7 +46,6 @@ async function httpStatus(url, timeoutMs = 20000) {
|
|
|
45
46
|
// entries from sysctl KERN_PROCARGS2) and must carry the testnet variable. An
|
|
46
47
|
// unreadable process makes the result unknown.
|
|
47
48
|
export const DEV_SERVER_PID_FILE = 'temp/farmslot/next-dev.pid';
|
|
48
|
-
const FORCE_VARIABLE = 'NEXT_PUBLIC_HYPERLIQUID_FORCE_TESTNET';
|
|
49
49
|
|
|
50
50
|
function run(command, args) {
|
|
51
51
|
try {
|
|
@@ -142,7 +142,7 @@ function chainTo(listener, root, parents) {
|
|
|
142
142
|
return null;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
export function devServerTestnetDiagnostic(target, { appPort, probe = systemProbe } = {}) {
|
|
145
|
+
export function devServerTestnetDiagnostic(target, { appPort, variable, probe = systemProbe } = {}) {
|
|
146
146
|
const checkout = canonicalPath(target);
|
|
147
147
|
const unknown = (detail) => ({ known: false, forced: false, detail });
|
|
148
148
|
const pidFile = path.join(checkout, DEV_SERVER_PID_FILE);
|
|
@@ -170,14 +170,14 @@ export function devServerTestnetDiagnostic(target, { appPort, probe = systemProb
|
|
|
170
170
|
const processMode = nextServerMode(args.argv);
|
|
171
171
|
if (processMode === 'start') return { known: true, forced: false, detail: `pid ${pid} runs next start; its environment cannot prove what the build inlined` };
|
|
172
172
|
if (processMode === 'dev') mode = 'dev';
|
|
173
|
-
const value = environmentValue(args.env,
|
|
173
|
+
const value = environmentValue(args.env, variable);
|
|
174
174
|
if (value !== 'true') {
|
|
175
|
-
return { known: true, forced: false, detail: `pid ${pid} (between app port ${appPort} and next dev pid ${root}) has ${
|
|
175
|
+
return { known: true, forced: false, detail: `pid ${pid} (between app port ${appPort} and next dev pid ${root}) has ${variable}=${value ?? '<unset>'}` };
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
if (mode !== 'dev') return { known: true, forced: false, detail: `no process serving app port ${appPort} runs next dev` };
|
|
180
|
-
return { known: true, forced: true, detail: `every process from app port ${appPort}'s listeners (cwd ${checkout}) up to next dev pid ${root} has ${
|
|
180
|
+
return { known: true, forced: true, detail: `every process from app port ${appPort}'s listeners (cwd ${checkout}) up to next dev pid ${root} has ${variable}=true` };
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
export const MAINNET_CONFIRMATION = 'REAL_FUNDS';
|
|
@@ -195,7 +195,15 @@ export function assertLaunchNetwork(_target, { network, mainnetConfirmation } =
|
|
|
195
195
|
return requested;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// "a, b and c".
|
|
199
|
+
function listing(names) {
|
|
200
|
+
return names.length > 1 ? `${names.slice(0, -1).join(', ')} and ${names.at(-1)}` : names.join('');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// `policy`: the venue policy (adapters/terminal/policy.mjs): dependencies,
|
|
204
|
+
// startPath, testnetVariable.
|
|
198
205
|
export async function terminalReadiness({
|
|
206
|
+
policy,
|
|
199
207
|
target,
|
|
200
208
|
appPort,
|
|
201
209
|
cdpPort,
|
|
@@ -207,17 +215,17 @@ export async function terminalReadiness({
|
|
|
207
215
|
const checks = [];
|
|
208
216
|
const shape = isTerminalCheckout(target);
|
|
209
217
|
checks.push(check('checkout', shape, shape ? 'Next.js checkout with src/features/perpetuals' : `${target} is not a Web Terminal checkout`));
|
|
210
|
-
const deps =
|
|
211
|
-
checks.push(check('dependencies', deps.length === 0, deps.length === 0 ?
|
|
218
|
+
const deps = policy.dependencies.filter((name) => !existsSync(path.join(target, 'node_modules', name, 'package.json')));
|
|
219
|
+
checks.push(check('dependencies', deps.length === 0, deps.length === 0 ? `${listing(policy.dependencies)} installed` : `missing: ${deps.join(', ')} (run npm ci on Node 22)`));
|
|
212
220
|
|
|
213
221
|
if (appPort) {
|
|
214
|
-
const status = await httpStatus(`http://127.0.0.1:${appPort}
|
|
215
|
-
checks.push(check('app-dev-server', status === 200, `GET http://localhost:${appPort}
|
|
222
|
+
const status = await httpStatus(`http://127.0.0.1:${appPort}${policy.startPath}`);
|
|
223
|
+
checks.push(check('app-dev-server', status === 200, `GET http://localhost:${appPort}${policy.startPath} -> ${status}`));
|
|
216
224
|
} else {
|
|
217
225
|
checks.push(check('app-dev-server', false, 'no dev-server port; pass --watcher-port <port> or set TERMINAL_APP_PORT'));
|
|
218
226
|
}
|
|
219
227
|
// Advisory: the browser enforces testnet itself (mainnet-guard.mjs).
|
|
220
|
-
const testnet = devServerTestnetDiagnostic(target, { appPort });
|
|
228
|
+
const testnet = devServerTestnetDiagnostic(target, { appPort, variable: policy.testnetVariable });
|
|
221
229
|
checks.push(check('testnet-forced', testnet.known && testnet.forced, testnet.detail, {
|
|
222
230
|
required: false,
|
|
223
231
|
status: testnet.known && testnet.forced ? 'pass' : 'warn',
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The Web Terminal's venue policy: what the generic web-dapp launch, wallet
|
|
2
|
+
// host and readiness need to know about Hyperliquid. They take it as an
|
|
3
|
+
// argument (the leaf scripts import it from --policy-module, reading its
|
|
4
|
+
// `policy` export), so none of them names the venue. It becomes the terminal
|
|
5
|
+
// plugin's `policy` once recipe libraries can declare adapters; the Terminal's
|
|
6
|
+
// detection and its signature-log rules (_signature-log.mjs) move with it.
|
|
7
|
+
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
import { LINK_HOSTS, TESTNET_TYPED_DATA_POLICY, venueHosts } from './lib/mainnet-guard.mjs';
|
|
11
|
+
|
|
12
|
+
export const policy = Object.freeze({
|
|
13
|
+
// network=testnet: the hosts the browser must never reach, and the hosts
|
|
14
|
+
// whose traffic proves the app serves testnet.
|
|
15
|
+
venueHosts(checkout) {
|
|
16
|
+
const { mainnet, testnet } = venueHosts(checkout);
|
|
17
|
+
return { blocked: mainnet, served: testnet };
|
|
18
|
+
},
|
|
19
|
+
// Venue web hosts (pages, coin icons): never blocked, never proof.
|
|
20
|
+
linkHosts: Object.freeze([...LINK_HOSTS]),
|
|
21
|
+
// The launch's proof that the block holds: requests to this blocked host
|
|
22
|
+
// (preferred; else the first blocked host) must fail.
|
|
23
|
+
probe: Object.freeze({ host: 'api.hyperliquid.xyz', httpPath: '/info', httpBody: '{"type":"meta"}', wsPath: '/ws' }),
|
|
24
|
+
// The chain the injected wallet starts on (Arbitrum One).
|
|
25
|
+
startChain: 42161,
|
|
26
|
+
// Typed data a testnet run never signs.
|
|
27
|
+
refuseTypedData: TESTNET_TYPED_DATA_POLICY,
|
|
28
|
+
// The app's first page: the launch opens it, readiness probes it.
|
|
29
|
+
startPath: '/order/BTC',
|
|
30
|
+
dependencies: Object.freeze(['next', '@nktkas/hyperliquid', '@metamask/perps-controller']),
|
|
31
|
+
// The dev-server environment variable that forces testnet (advisory check).
|
|
32
|
+
testnetVariable: 'NEXT_PUBLIC_HYPERLIQUID_FORCE_TESTNET',
|
|
33
|
+
// This module, for the wallet host (a separate process).
|
|
34
|
+
module: fileURLToPath(import.meta.url),
|
|
35
|
+
});
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
// Web Terminal slot (checkout, deps, dev server, forced testnet, fixture,
|
|
4
4
|
// browser start probe, MetaMask extension copy, CDP port).
|
|
5
5
|
//
|
|
6
|
-
// Inputs: --target <checkout> [--cdp-port <port>] [--watcher-port <app port>]
|
|
6
|
+
// Inputs: --policy-module <venue policy module> --target <checkout> [--cdp-port <port>] [--watcher-port <app port>]
|
|
7
7
|
// [--signer extension|injected] [--account <name>] [--json]
|
|
8
8
|
// Outputs: readiness JSON (or a human summary) on stdout. Exit 0 ready; 1 not ready.
|
|
9
9
|
// Never launches the slot browser or the dev server.
|
|
10
10
|
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
13
|
|
|
14
14
|
import { resolveBrowser } from './launch.mjs';
|
|
15
15
|
import { terminalReadiness } from './lib/readiness.mjs';
|
|
16
16
|
|
|
17
|
-
const usage = 'Usage: verify.mjs --target <checkout> [--cdp-port <port>] [--watcher-port <app port>] [--signer extension|injected] [--account <name>] [--json]';
|
|
17
|
+
const usage = 'Usage: verify.mjs --policy-module <venue policy module> --target <checkout> [--cdp-port <port>] [--watcher-port <app port>] [--signer extension|injected] [--account <name>] [--json]';
|
|
18
18
|
|
|
19
19
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
20
20
|
const argv = process.argv.slice(2);
|
|
@@ -27,7 +27,14 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
|
|
|
27
27
|
return index >= 0 ? argv[index + 1] : undefined;
|
|
28
28
|
};
|
|
29
29
|
const num = (raw) => (raw && /^\d+$/u.test(raw) ? Number(raw) : undefined);
|
|
30
|
+
const policyModule = value('--policy-module');
|
|
31
|
+
if (!policyModule) {
|
|
32
|
+
process.stderr.write(`Missing --policy-module\n${usage}\n`);
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
const { policy } = await import(pathToFileURL(path.resolve(policyModule)).href);
|
|
30
36
|
const report = await terminalReadiness({
|
|
37
|
+
policy,
|
|
31
38
|
target: path.resolve(value('--target') ?? process.cwd()),
|
|
32
39
|
appPort: num(value('--watcher-port') ?? process.env.TERMINAL_APP_PORT ?? process.env.WATCHER_PORT),
|
|
33
40
|
cdpPort: num(value('--cdp-port') ?? process.env.RECIPE_CDP_PORT ?? process.env.CDP_PORT),
|