@deeeed/metamask-harness 0.2.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 +161 -0
- package/README.md +140 -0
- package/bin/mm-harness +99 -0
- package/docs/CHEATSHEET.md +61 -0
- package/docs/CLI-SPEC.md +915 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/architecture.md +367 -0
- package/docs/extension-runtime-commands.md +60 -0
- package/docs/harness-cli.md +43 -0
- package/docs/live-adapter-contract.md +188 -0
- package/docs/package-boundaries.md +47 -0
- package/docs/perps-flow-catalog.md +235 -0
- package/docs/recipe-libraries.md +95 -0
- package/docs/runtime-file-conventions.md +36 -0
- package/library/actions/core/perps/_controller.mjs +727 -0
- package/library/actions/core/perps/assert_orders.mjs +53 -0
- package/library/actions/core/perps/assert_positions.mjs +52 -0
- package/library/actions/core/perps/close_orders.mjs +97 -0
- package/library/actions/core/perps/close_positions.mjs +118 -0
- package/library/actions/core/perps/ensure_orders.mjs +40 -0
- package/library/actions/core/perps/ensure_positions.mjs +37 -0
- package/library/actions/core/perps/place_order.mjs +201 -0
- package/library/actions/core/perps/read_account.mjs +30 -0
- package/library/actions/core/perps/read_orders.mjs +27 -0
- package/library/actions/core/perps/read_positions.mjs +27 -0
- package/library/actions/core/perps/start_state.mjs +92 -0
- package/library/actions/core/perps/teardown_state.mjs +86 -0
- package/library/actions/extension/perps/assert_orders.mjs +11 -0
- package/library/actions/extension/perps/assert_positions.mjs +11 -0
- package/library/actions/extension/perps/close_orders.mjs +8 -0
- package/library/actions/extension/perps/close_positions.mjs +8 -0
- package/library/actions/extension/perps/ensure_orders.mjs +4 -0
- package/library/actions/extension/perps/ensure_positions.mjs +4 -0
- package/library/actions/extension/perps/perps.mjs +730 -0
- package/library/actions/extension/perps/place_order.mjs +7 -0
- package/library/actions/extension/perps/read_orders.mjs +4 -0
- package/library/actions/extension/perps/read_positions.mjs +3 -0
- package/library/actions/extension/platform/cdp.mjs +541 -0
- package/library/actions/extension/ui/navigate.mjs +44 -0
- package/library/actions/extension/wallet/ensure_unlocked.mjs +36 -0
- package/library/actions/extension/wallet/read_state.mjs +27 -0
- package/library/actions/extension/wallet/select_account.mjs +48 -0
- package/library/actions/extension/wallet/setup.mjs +35 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/library/actions/mobile/bridge-runtime/cdp-bridge.cjs +686 -0
- package/library/actions/mobile/bridge-runtime/lib/cdp-eval.cjs +110 -0
- package/library/actions/mobile/bridge-runtime/lib/config.cjs +39 -0
- package/library/actions/mobile/bridge-runtime/lib/issue-capture.cjs +446 -0
- package/library/actions/mobile/bridge-runtime/lib/target-discovery.cjs +204 -0
- package/library/actions/mobile/bridge-runtime/lib/ws-client.cjs +108 -0
- package/library/actions/mobile/bridge-runtime/setup-wallet.sh +442 -0
- package/library/actions/mobile/perps/assert_orders.mjs +11 -0
- package/library/actions/mobile/perps/assert_positions.mjs +11 -0
- package/library/actions/mobile/perps/close_orders.mjs +8 -0
- package/library/actions/mobile/perps/close_positions.mjs +8 -0
- package/library/actions/mobile/perps/ensure_orders.mjs +4 -0
- package/library/actions/mobile/perps/ensure_positions.mjs +4 -0
- package/library/actions/mobile/perps/perps.mjs +709 -0
- package/library/actions/mobile/perps/place_order.mjs +7 -0
- package/library/actions/mobile/perps/read_orders.mjs +4 -0
- package/library/actions/mobile/perps/read_positions.mjs +3 -0
- package/library/actions/mobile/platform/bridge.mjs +283 -0
- package/library/actions/mobile/ui/navigate.mjs +38 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +107 -0
- package/library/actions/mobile/wallet/home.mjs +35 -0
- package/library/actions/mobile/wallet/read_state.mjs +40 -0
- package/library/actions/mobile/wallet/select_account.mjs +48 -0
- package/library/actions/mobile/wallet/setup.mjs +220 -0
- package/library/flows/perps.flows.json +64 -0
- package/library/library.json +7 -0
- package/library/manifests/core.action-manifest.json +1282 -0
- package/library/manifests/extension.action-manifest.json +1749 -0
- package/library/manifests/mobile.action-manifest.json +1753 -0
- package/library/recipes/action-validation.extension.recipe.json +417 -0
- package/library/recipes/action-validation.mobile.recipe.json +422 -0
- package/library/recipes/order-lifecycle.core.recipe.json +78 -0
- package/library/recipes/perps-lifecycle.recipe.json +194 -0
- package/library/recipes/read-markets.core.recipe.json +38 -0
- package/library/recipes/smoke.extension.recipe.json +31 -0
- package/library/recipes/smoke.mobile.recipe.json +31 -0
- package/library/recipes/trading-lifecycle.core.recipe.json +76 -0
- package/orchestration/compat-overlays/README.md +19 -0
- package/orchestration/compat-overlays/mobile/README.md +13 -0
- package/orchestration/compat-overlays/mobile/rn81-message-event-source.patch +42 -0
- package/orchestration/core/cleanup.sh +37 -0
- package/orchestration/core/inject.sh +154 -0
- package/orchestration/doctor.mjs +72 -0
- package/orchestration/extension/cleanup.mjs +60 -0
- package/orchestration/extension/console-tail.mjs +228 -0
- package/orchestration/extension/ensure-browser.sh +416 -0
- package/orchestration/extension/ensure-ready.ts +185 -0
- package/orchestration/extension/extension-id.ts +107 -0
- package/orchestration/extension/inject.mjs +266 -0
- package/orchestration/extension/launch-browser.cjs +216 -0
- package/orchestration/extension/launch.sh +175 -0
- package/orchestration/extension/live.sh +320 -0
- package/orchestration/extension/pin-remote-flags.cjs +45 -0
- package/orchestration/extension/readiness.mjs +414 -0
- package/orchestration/extension/refresh-build.sh +190 -0
- package/orchestration/extension/runtime-decision.ts +445 -0
- package/orchestration/extension/runtime.ts +407 -0
- package/orchestration/extension/seed-fixture.sh +177 -0
- package/orchestration/extension/sidepanel-toggle.sh +291 -0
- package/orchestration/extension/snapshot-dist.sh +84 -0
- package/orchestration/extension/start-watch.sh +339 -0
- package/orchestration/extension/wallet-fixture-state.cjs +1086 -0
- package/orchestration/lib/activate-repo-node.sh +144 -0
- package/orchestration/lib/cli-color.mjs +84 -0
- package/orchestration/lib/cli-commands.mjs +243 -0
- package/orchestration/lib/cli-home.mjs +354 -0
- package/orchestration/lib/cli-ux.sh +252 -0
- package/orchestration/lib/cli-version.mjs +123 -0
- package/orchestration/lib/ensure-runner-deps.sh +56 -0
- package/orchestration/lib/harness-path.sh +55 -0
- package/orchestration/lib/hash-helpers.sh +44 -0
- package/orchestration/lib/json-field.sh +23 -0
- package/orchestration/lib/log-tui.mjs +304 -0
- package/orchestration/lib/open-debug.mjs +317 -0
- package/orchestration/lib/path-defaults.json +4 -0
- package/orchestration/lib/progress.mjs +107 -0
- package/orchestration/lib/recipe-paths.mjs +26 -0
- package/orchestration/lib/resolve-farmslot-ports.sh +144 -0
- package/orchestration/manifest.json +358 -0
- package/orchestration/mobile/cleanup.sh +192 -0
- package/orchestration/mobile/deps-markers.ts +21 -0
- package/orchestration/mobile/inject.sh +681 -0
- package/orchestration/mobile/launch.sh +137 -0
- package/orchestration/mobile/live.sh +125 -0
- package/orchestration/mobile/runtime-decision.ts +292 -0
- package/orchestration/porcelain/metamask-recipe +99 -0
- package/orchestration/porcelain/mm-recipe +1591 -0
- package/orchestration/porcelain/mme-recipe +1181 -0
- package/package.json +59 -0
- package/runner/extension/verify.sh +511 -0
- package/runner/mobile/verify.sh +501 -0
- package/runner/src/adapters.ts +601 -0
- package/runner/src/cli.ts +1820 -0
- package/runner/src/commands/debug.ts +44 -0
- package/runner/src/commands/fixtures.ts +99 -0
- package/runner/src/commands/launch.ts +397 -0
- package/runner/src/commands/logs.ts +60 -0
- package/runner/src/commands/shared.ts +138 -0
- package/runner/src/completions-cache.ts +86 -0
- package/runner/src/doctor.ts +203 -0
- package/runner/src/harness.ts +516 -0
- package/runner/src/heal-bounds.ts +179 -0
- package/runner/src/index.ts +6 -0
- package/runner/src/live-adapter-contract.ts +274 -0
- package/runner/src/manifest.ts +47 -0
- package/runner/src/mm-harness-cli.ts +488 -0
- package/runner/src/paths.ts +198 -0
- package/runner/src/recording-target.ts +147 -0
- package/runner/src/run-recording.ts +329 -0
- package/runner/src/runner.ts +108 -0
- package/runner/src/types.ts +57 -0
- package/scripts/completions.sh +125 -0
- package/scripts/install-completions.sh +62 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# launch.sh — mobile instance identity intake + prepare/preflight launch
|
|
3
|
+
#
|
|
4
|
+
# Purpose:
|
|
5
|
+
# Resolves the per-instance tuple (port, simulator/adb-serial, runtime dir,
|
|
6
|
+
# platform, preflight mode) and runs the installed runner's mobile prepare.
|
|
7
|
+
#
|
|
8
|
+
# Inputs (flags / env precedence):
|
|
9
|
+
# --target <metamask-mobile> (default $PWD)
|
|
10
|
+
# --platform ios|android (default ios)
|
|
11
|
+
# --preflight-mode|--mode fast|auto|default|rebuild-native|clean
|
|
12
|
+
# (env RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE, default fast)
|
|
13
|
+
# --port|--watcher-port|--cdp-port <port> (env WATCHER_PORT > METRO_PORT > CDP_PORT)
|
|
14
|
+
# --simulator <sim> (env IOS_SIMULATOR > SIM_UDID)
|
|
15
|
+
# --adb-serial <serial> (env ADB_SERIAL > ANDROID_SERIAL)
|
|
16
|
+
# --runtime-dir <dir> (env RECIPE_RUNTIME_DIR)
|
|
17
|
+
# --wallet-setup / --no-wallet-setup, --wallet-fixture <json>
|
|
18
|
+
# --artifacts-dir <dir>
|
|
19
|
+
#
|
|
20
|
+
# Outputs:
|
|
21
|
+
# <artifacts>/summary.json (adapter/action/status/runtime/generatedAt),
|
|
22
|
+
# <artifacts>/runtime-status.json, <artifacts>/logs/prepare.log
|
|
23
|
+
# Exit 0 — launch pass; 1 — runner missing or prepare failed; 2 — bad args.
|
|
24
|
+
#
|
|
25
|
+
# Never touches: product source files; anything outside the target's harness
|
|
26
|
+
# dir and the artifacts dir.
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
TARGET="$PWD"
|
|
30
|
+
PLATFORM="ios"
|
|
31
|
+
PREFLIGHT_MODE="${RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE:-fast}"
|
|
32
|
+
ARTIFACTS=""
|
|
33
|
+
PORT="${WATCHER_PORT:-${METRO_PORT:-${CDP_PORT:-}}}"
|
|
34
|
+
SIMULATOR="${IOS_SIMULATOR:-${SIM_UDID:-}}"
|
|
35
|
+
ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
|
|
36
|
+
RUNTIME_DIR_ARG="${RECIPE_RUNTIME_DIR:-}"
|
|
37
|
+
WALLET_SETUP=false
|
|
38
|
+
WALLET_FIXTURE_ARG=""
|
|
39
|
+
require_value() { [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; }
|
|
40
|
+
while [ "$#" -gt 0 ]; do
|
|
41
|
+
case "$1" in
|
|
42
|
+
--target) require_value "$@"; TARGET="$2"; shift 2 ;;
|
|
43
|
+
--platform) require_value "$@"; PLATFORM="$2"; shift 2 ;;
|
|
44
|
+
--preflight-mode|--mode) require_value "$@"; PREFLIGHT_MODE="$2"; shift 2 ;;
|
|
45
|
+
--artifacts-dir) require_value "$@"; ARTIFACTS="$2"; shift 2 ;;
|
|
46
|
+
--port|--watcher-port|--cdp-port) require_value "$@"; PORT="$2"; shift 2 ;;
|
|
47
|
+
--simulator) require_value "$@"; SIMULATOR="$2"; shift 2 ;;
|
|
48
|
+
--adb-serial) require_value "$@"; ADB_SERIAL_ARG="$2"; shift 2 ;;
|
|
49
|
+
--runtime-dir) require_value "$@"; RUNTIME_DIR_ARG="$2"; shift 2 ;;
|
|
50
|
+
--wallet-setup) WALLET_SETUP=true; shift ;;
|
|
51
|
+
--no-wallet-setup) WALLET_SETUP=false; shift ;;
|
|
52
|
+
--wallet-fixture) require_value "$@"; WALLET_FIXTURE_ARG="$2"; shift 2 ;;
|
|
53
|
+
-h|--help)
|
|
54
|
+
echo "Usage: launch.sh [--target <metamask-mobile>] [--platform ios|android] [--preflight-mode fast|auto|default|rebuild-native|clean] [--port <port>] [--simulator <sim>] [--runtime-dir <dir>] [--wallet-setup] [--wallet-fixture <json>]"
|
|
55
|
+
exit 0
|
|
56
|
+
;;
|
|
57
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
58
|
+
esac
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
case "$PLATFORM" in ios|android) ;; *) echo "Unknown --platform: $PLATFORM" >&2; exit 2 ;; esac
|
|
62
|
+
case "$PREFLIGHT_MODE" in fast|auto|default|rebuild-native|clean) ;; *) echo "Unknown --preflight-mode: $PREFLIGHT_MODE" >&2; exit 2 ;; esac
|
|
63
|
+
|
|
64
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
65
|
+
# shellcheck disable=SC1091
|
|
66
|
+
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
67
|
+
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
68
|
+
done
|
|
69
|
+
unset _hp
|
|
70
|
+
if ! command -v harness_root >/dev/null 2>&1; then
|
|
71
|
+
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
75
|
+
HARNESS_DIR="$(harness_dir "$TARGET" mobile)"
|
|
76
|
+
RUNNER_BIN="$HARNESS_DIR/runner/bin/metamask-recipe"
|
|
77
|
+
ARTIFACTS="${ARTIFACTS:-$HARNESS_DIR/launch/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
78
|
+
mkdir -p "$ARTIFACTS/logs"
|
|
79
|
+
|
|
80
|
+
if [ ! -x "$RUNNER_BIN" ]; then
|
|
81
|
+
echo "Mobile recipe runner is not installed in $TARGET. Run metamask-recipe mobile install --target $TARGET first." >&2
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
prepare_args=(mobile prepare --target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE")
|
|
86
|
+
[ -n "$PORT" ] && prepare_args+=(--port "$PORT")
|
|
87
|
+
[ -n "$SIMULATOR" ] && prepare_args+=(--simulator "$SIMULATOR")
|
|
88
|
+
[ -n "$ADB_SERIAL_ARG" ] && prepare_args+=(--adb-serial "$ADB_SERIAL_ARG")
|
|
89
|
+
[ -n "$RUNTIME_DIR_ARG" ] && prepare_args+=(--runtime-dir "$RUNTIME_DIR_ARG")
|
|
90
|
+
$WALLET_SETUP && prepare_args+=(--wallet-setup)
|
|
91
|
+
[ -n "$WALLET_FIXTURE_ARG" ] && prepare_args+=(--wallet-fixture "$WALLET_FIXTURE_ARG")
|
|
92
|
+
|
|
93
|
+
status="pass"
|
|
94
|
+
if (cd "$TARGET" && "$RUNNER_BIN" "${prepare_args[@]}") > "$ARTIFACTS/logs/prepare.log" 2>&1; then
|
|
95
|
+
runtime_status="pass"
|
|
96
|
+
else
|
|
97
|
+
runtime_status="fail"
|
|
98
|
+
status="fail"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
status_args=(mobile runtime-status --target "$TARGET" --json)
|
|
102
|
+
[ -n "$PORT" ] && status_args+=(--port "$PORT")
|
|
103
|
+
[ -n "$RUNTIME_DIR_ARG" ] && status_args+=(--runtime-dir "$RUNTIME_DIR_ARG")
|
|
104
|
+
(cd "$TARGET" && "$RUNNER_BIN" "${status_args[@]}") > "$ARTIFACTS/runtime-status.json" 2> "$ARTIFACTS/logs/runtime-status.err" || true
|
|
105
|
+
|
|
106
|
+
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" STATUS_FOR_SUMMARY="$status" RUNTIME_STATUS="$runtime_status" PLATFORM_FOR_SUMMARY="$PLATFORM" MODE_FOR_SUMMARY="$PREFLIGHT_MODE" node <<'NODE'
|
|
107
|
+
const fs = require('fs');
|
|
108
|
+
const path = require('path');
|
|
109
|
+
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
110
|
+
let runtime = null;
|
|
111
|
+
try { runtime = JSON.parse(fs.readFileSync(path.join(artifacts, 'runtime-status.json'), 'utf8')); } catch {}
|
|
112
|
+
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
113
|
+
adapter: 'mobile',
|
|
114
|
+
action: 'launch',
|
|
115
|
+
status: process.env.STATUS_FOR_SUMMARY,
|
|
116
|
+
platform: process.env.PLATFORM_FOR_SUMMARY,
|
|
117
|
+
preflightMode: process.env.MODE_FOR_SUMMARY,
|
|
118
|
+
target: process.env.TARGET_FOR_SUMMARY,
|
|
119
|
+
runtime: {
|
|
120
|
+
status: process.env.RUNTIME_STATUS,
|
|
121
|
+
ready: runtime?.ready === true,
|
|
122
|
+
reason: runtime?.reason || null,
|
|
123
|
+
port: runtime?.metro?.port || null,
|
|
124
|
+
prepareLogPath: path.join(artifacts, 'logs/prepare.log'),
|
|
125
|
+
runtimeStatusPath: path.join(artifacts, 'runtime-status.json'),
|
|
126
|
+
},
|
|
127
|
+
runtimePolicy: {
|
|
128
|
+
nativeBuildPolicy: process.env.MODE_FOR_SUMMARY === 'fast'
|
|
129
|
+
? 'fast mode never runs a native build; it requires an installed dev client, starts/reuses Metro, prewarms the JS bundle, opens the dev client, and waits for the bridge'
|
|
130
|
+
: 'non-fast modes may install or rebuild the native dev client when needed; then start/reuse Metro, prewarm the JS bundle, open the dev client, and wait for the bridge',
|
|
131
|
+
},
|
|
132
|
+
generatedAt: new Date().toISOString(),
|
|
133
|
+
}, null, 2)}\n`);
|
|
134
|
+
NODE
|
|
135
|
+
|
|
136
|
+
echo "Mobile harness launch $status: $ARTIFACTS/summary.json"
|
|
137
|
+
[ "$status" = "pass" ]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# live.sh — mobile launch-then-verify chaining for one live slot run
|
|
3
|
+
#
|
|
4
|
+
# Purpose:
|
|
5
|
+
# Runs launch.sh then verify.sh (verify with --no-auto-start; launch owns
|
|
6
|
+
# startup) under a timestamped artifact dir and aggregates both results.
|
|
7
|
+
#
|
|
8
|
+
# Inputs (flags / env):
|
|
9
|
+
# --target <metamask-mobile> (default $PWD)
|
|
10
|
+
# --platform ios|android (default ios)
|
|
11
|
+
# --preflight-mode|--mode fast|auto|default|rebuild-native|clean
|
|
12
|
+
# (env RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE, default fast)
|
|
13
|
+
# --artifacts-dir <dir> (default <harness>/live/<UTC>)
|
|
14
|
+
# --wallet-setup|--no-wallet-setup (default auto), --wallet-fixture <json>
|
|
15
|
+
#
|
|
16
|
+
# Outputs:
|
|
17
|
+
# <artifacts>/summary.json (adapter/action/status, launch/verify exit codes
|
|
18
|
+
# + child summary paths, easyCommand), <artifacts>/{launch,verify}/.
|
|
19
|
+
# Exit 0 — launch and verify both passed; 1 — either failed; 2 — bad args.
|
|
20
|
+
#
|
|
21
|
+
# Never touches: product source files; anything outside the target's harness
|
|
22
|
+
# dir and the artifacts dir.
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
TARGET="$PWD"
|
|
26
|
+
PLATFORM="ios"
|
|
27
|
+
PREFLIGHT_MODE="${RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE:-fast}"
|
|
28
|
+
ARTIFACTS=""
|
|
29
|
+
WALLET_SETUP="auto"
|
|
30
|
+
WALLET_FIXTURE=""
|
|
31
|
+
while [ "$#" -gt 0 ]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
34
|
+
--platform) PLATFORM="$2"; shift 2 ;;
|
|
35
|
+
--preflight-mode|--mode) PREFLIGHT_MODE="$2"; shift 2 ;;
|
|
36
|
+
--artifacts-dir) ARTIFACTS="$2"; shift 2 ;;
|
|
37
|
+
--wallet-setup) WALLET_SETUP="true"; shift ;;
|
|
38
|
+
--no-wallet-setup) WALLET_SETUP="false"; shift ;;
|
|
39
|
+
--wallet-fixture) WALLET_FIXTURE="$2"; shift 2 ;;
|
|
40
|
+
-h|--help) echo "Usage: live.sh [--target <metamask-mobile>] [--platform ios|android] [--preflight-mode fast|auto|default|rebuild-native|clean] [--artifacts-dir <dir>] [--wallet-setup|--no-wallet-setup]"; exit 0 ;;
|
|
41
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
42
|
+
esac
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
case "$PLATFORM" in ios|android) ;; *) echo "Unknown --platform: $PLATFORM" >&2; exit 2 ;; esac
|
|
46
|
+
case "$PREFLIGHT_MODE" in fast|auto|default|rebuild-native|clean) ;; *) echo "Unknown --preflight-mode: $PREFLIGHT_MODE" >&2; exit 2 ;; esac
|
|
47
|
+
|
|
48
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
49
|
+
# shellcheck disable=SC1091
|
|
50
|
+
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
51
|
+
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
52
|
+
done
|
|
53
|
+
unset _hp
|
|
54
|
+
if ! command -v harness_root >/dev/null 2>&1; then
|
|
55
|
+
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
# verify.sh is a recipe-tree feature: co-located in installed copies, under
|
|
59
|
+
# runner/mobile in a repo checkout.
|
|
60
|
+
VERIFY_SH=""
|
|
61
|
+
for _v in "$SCRIPT_DIR/verify.sh" "$SCRIPT_DIR/../../runner/mobile/verify.sh"; do
|
|
62
|
+
[ -f "$_v" ] && { VERIFY_SH="$_v"; break; }
|
|
63
|
+
done
|
|
64
|
+
unset _v
|
|
65
|
+
if [ -z "$VERIFY_SH" ]; then
|
|
66
|
+
echo "mm-harness: mobile verify.sh not found next to $SCRIPT_DIR; reinstall the runner." >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
70
|
+
WALLET_FIXTURE="${WALLET_FIXTURE:-$(recipe_runtime_dir)/wallet-fixture.json}"
|
|
71
|
+
ARTIFACTS="${ARTIFACTS:-$(harness_dir "$TARGET" mobile)/live/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
72
|
+
mkdir -p "$ARTIFACTS"
|
|
73
|
+
|
|
74
|
+
launch_args=(--target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE" --artifacts-dir "$ARTIFACTS/launch")
|
|
75
|
+
case "$WALLET_SETUP" in
|
|
76
|
+
true) launch_args+=(--wallet-setup) ;;
|
|
77
|
+
false) launch_args+=(--no-wallet-setup) ;;
|
|
78
|
+
esac
|
|
79
|
+
launch_args+=(--wallet-fixture "$WALLET_FIXTURE")
|
|
80
|
+
|
|
81
|
+
echo "Mobile live validation command:"
|
|
82
|
+
echo " mm-harness launch $PLATFORM --verify"
|
|
83
|
+
echo "Launch artifacts: $ARTIFACTS/launch"
|
|
84
|
+
echo "Verify artifacts: $ARTIFACTS/verify"
|
|
85
|
+
|
|
86
|
+
set +e
|
|
87
|
+
"$SCRIPT_DIR/launch.sh" "${launch_args[@]}"
|
|
88
|
+
launch_status=$?
|
|
89
|
+
set -e
|
|
90
|
+
|
|
91
|
+
verify_status=1
|
|
92
|
+
if [ "$launch_status" -eq 0 ]; then
|
|
93
|
+
set +e
|
|
94
|
+
"$VERIFY_SH" --target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE" --no-auto-start --artifacts-dir "$ARTIFACTS/verify"
|
|
95
|
+
verify_status=$?
|
|
96
|
+
set -e
|
|
97
|
+
else
|
|
98
|
+
echo "Skipping Mobile live verify because launch failed; see $ARTIFACTS/launch/summary.json" >&2
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" PLATFORM_FOR_SUMMARY="$PLATFORM" MODE_FOR_SUMMARY="$PREFLIGHT_MODE" LAUNCH_STATUS="$launch_status" VERIFY_STATUS="$verify_status" node <<'NODE'
|
|
102
|
+
const fs = require('fs');
|
|
103
|
+
const path = require('path');
|
|
104
|
+
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
105
|
+
const launchSummary = path.join(artifacts, 'launch', 'summary.json');
|
|
106
|
+
const verifySummary = path.join(artifacts, 'verify', 'summary.json');
|
|
107
|
+
const launchStatus = Number(process.env.LAUNCH_STATUS);
|
|
108
|
+
const verifyStatus = Number(process.env.VERIFY_STATUS);
|
|
109
|
+
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
110
|
+
adapter: 'mobile',
|
|
111
|
+
action: 'live',
|
|
112
|
+
status: launchStatus === 0 && verifyStatus === 0 ? 'pass' : 'fail',
|
|
113
|
+
target: process.env.TARGET_FOR_SUMMARY,
|
|
114
|
+
platform: process.env.PLATFORM_FOR_SUMMARY,
|
|
115
|
+
preflightMode: process.env.MODE_FOR_SUMMARY,
|
|
116
|
+
launch: { exitCode: launchStatus, summaryPath: fs.existsSync(launchSummary) ? launchSummary : null },
|
|
117
|
+
verify: { exitCode: verifyStatus, summaryPath: fs.existsSync(verifySummary) ? verifySummary : null },
|
|
118
|
+
easyCommand: `mm-harness launch ${process.env.PLATFORM_FOR_SUMMARY} --verify`,
|
|
119
|
+
note: 'Runs launch then live verify so a developer can validate app startup, runner-owned CDP bridge, screenshot capture, and tiny recipe control from one runner-owned command.',
|
|
120
|
+
generatedAt: new Date().toISOString(),
|
|
121
|
+
}, null, 2)}\n`);
|
|
122
|
+
NODE
|
|
123
|
+
|
|
124
|
+
echo "Mobile live validation summary: $ARTIFACTS/summary.json"
|
|
125
|
+
[ "$launch_status" -eq 0 ] && [ "$verify_status" -eq 0 ]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
clearDecisionState,
|
|
6
|
+
depsCheck,
|
|
7
|
+
recordDepsBaseline,
|
|
8
|
+
type DepsCheck,
|
|
9
|
+
} from '@farmslot/recipe-harness/runtime/deps-readiness';
|
|
10
|
+
import {
|
|
11
|
+
analyzeBundleLog,
|
|
12
|
+
evaluatePersistentBundleError,
|
|
13
|
+
supersededErrorCapture,
|
|
14
|
+
type BundleLogAnalysis,
|
|
15
|
+
} from '@farmslot/recipe-harness/runtime/log-analysis';
|
|
16
|
+
import { probeMetroPackager, type MetroReachabilityCheck } from '@farmslot/recipe-harness/runtime/metro-probe';
|
|
17
|
+
|
|
18
|
+
import { mobileProductMarkers } from './deps-markers.ts';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* MetaMask Mobile runtime-readiness adapter.
|
|
22
|
+
*
|
|
23
|
+
* Generic deps/log/metro primitives live in `@farmslot/recipe-harness/runtime/*`.
|
|
24
|
+
* This file owns MetaMask-specific markers, Nitro staleness regex, and action ids.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export type MobileReadinessDecision = 'install' | 'launch' | 'ready' | 'blocked' | 'unknown';
|
|
28
|
+
|
|
29
|
+
export interface MobileRuntimeDecisionAction {
|
|
30
|
+
id: string;
|
|
31
|
+
argv?: string[];
|
|
32
|
+
cwd?: string;
|
|
33
|
+
paths?: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type MetroLogCheck = BundleLogAnalysis;
|
|
37
|
+
|
|
38
|
+
export interface MobileRuntimeDecisionReport {
|
|
39
|
+
schemaVersion: 1;
|
|
40
|
+
adapter: 'mobile';
|
|
41
|
+
target: string;
|
|
42
|
+
decision: MobileReadinessDecision;
|
|
43
|
+
reasonCode: string;
|
|
44
|
+
reasons: string[];
|
|
45
|
+
checks: {
|
|
46
|
+
deps: DepsCheck;
|
|
47
|
+
metroLog: MetroLogCheck;
|
|
48
|
+
metro: MetroReachabilityCheck;
|
|
49
|
+
};
|
|
50
|
+
actions: MobileRuntimeDecisionAction[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface MobileRuntimeDecisionOptions {
|
|
54
|
+
watcherPort?: number;
|
|
55
|
+
metroLog?: string;
|
|
56
|
+
platform?: string;
|
|
57
|
+
record?: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const BUNDLE_ERR = /Bundling failed|Unable to resolve /u;
|
|
61
|
+
const BUNDLE_OK = /Bundled \d+ms|iOS Bundled|Android Bundled|Finished bundling/u;
|
|
62
|
+
const NATIVE_MODULE_STALE =
|
|
63
|
+
/\[runtime not ready\].*HybridObject "([^"]+)" - It has not yet been registered in the Nitro Modules HybridObjectRegistry/u;
|
|
64
|
+
|
|
65
|
+
function resolveMetroLog(target: string, metroLog?: string): string | null {
|
|
66
|
+
const rel = metroLog ?? 'temp/recipe/runtime/metro.log';
|
|
67
|
+
const abs = path.isAbsolute(rel) ? rel : path.join(target, rel);
|
|
68
|
+
return fs.existsSync(abs) ? abs : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function metroLogCheck(target: string, metroLog?: string): MetroLogCheck {
|
|
72
|
+
const abs = resolveMetroLog(target, metroLog);
|
|
73
|
+
if (!abs) return { status: 'no-log' };
|
|
74
|
+
const logText = fs.readFileSync(abs, 'utf8');
|
|
75
|
+
return analyzeBundleLog({
|
|
76
|
+
target,
|
|
77
|
+
logText,
|
|
78
|
+
errorPattern: BUNDLE_ERR,
|
|
79
|
+
okPattern: BUNDLE_OK,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const installActions = (target: string): MobileRuntimeDecisionAction[] => [
|
|
84
|
+
{ id: 'yarn-setup', argv: ['yarn', 'setup'], cwd: target },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const launchActions = (target: string, clearMetro = false): MobileRuntimeDecisionAction[] => {
|
|
88
|
+
const actions: MobileRuntimeDecisionAction[] = [];
|
|
89
|
+
if (clearMetro) {
|
|
90
|
+
actions.push({ id: 'clear-metro-cache', argv: ['yarn', 'expo', 'start', '--clear'], cwd: target });
|
|
91
|
+
}
|
|
92
|
+
actions.push({ id: 'launch-mobile-runtime' });
|
|
93
|
+
return actions;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export async function decideMobileReadiness(
|
|
97
|
+
target: string,
|
|
98
|
+
options: MobileRuntimeDecisionOptions = {},
|
|
99
|
+
): Promise<MobileRuntimeDecisionReport> {
|
|
100
|
+
const resolved = path.resolve(target);
|
|
101
|
+
if (options.record) recordDepsBaseline(resolved);
|
|
102
|
+
|
|
103
|
+
const deps = depsCheck(resolved, {
|
|
104
|
+
productMarkers: mobileProductMarkers(options.platform),
|
|
105
|
+
});
|
|
106
|
+
const metroLog = metroLogCheck(resolved, options.metroLog);
|
|
107
|
+
const metro = options.watcherPort
|
|
108
|
+
? await probeMetroPackager(options.watcherPort)
|
|
109
|
+
: { status: 'skipped' as const };
|
|
110
|
+
|
|
111
|
+
if (metroLog.status === 'ok') {
|
|
112
|
+
clearDecisionState(resolved, 'bundle-error-state.json');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const checks = { deps, metroLog, metro };
|
|
116
|
+
|
|
117
|
+
if (deps.status === 'missing') {
|
|
118
|
+
return {
|
|
119
|
+
schemaVersion: 1,
|
|
120
|
+
adapter: 'mobile',
|
|
121
|
+
target: resolved,
|
|
122
|
+
decision: 'install',
|
|
123
|
+
reasonCode: 'deps-missing',
|
|
124
|
+
reasons: ['Dependencies are not installed (no yarn install-state markers).'],
|
|
125
|
+
checks,
|
|
126
|
+
actions: installActions(resolved),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (deps.status === 'partial') {
|
|
131
|
+
const missing = deps.missingProducts?.join(', ') ?? 'product markers';
|
|
132
|
+
return {
|
|
133
|
+
schemaVersion: 1,
|
|
134
|
+
adapter: 'mobile',
|
|
135
|
+
target: resolved,
|
|
136
|
+
decision: 'install',
|
|
137
|
+
reasonCode: 'deps-partial',
|
|
138
|
+
reasons: [`node_modules tree is incomplete (missing: ${missing}).`],
|
|
139
|
+
checks,
|
|
140
|
+
actions: installActions(resolved),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (deps.status === 'stale') {
|
|
145
|
+
return {
|
|
146
|
+
schemaVersion: 1,
|
|
147
|
+
adapter: 'mobile',
|
|
148
|
+
target: resolved,
|
|
149
|
+
decision: 'install',
|
|
150
|
+
reasonCode: 'deps-stale',
|
|
151
|
+
reasons: ['package.json/yarn.lock changed since the recorded install baseline.'],
|
|
152
|
+
checks,
|
|
153
|
+
actions: installActions(resolved),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const metroLogAbs = resolveMetroLog(resolved, options.metroLog);
|
|
158
|
+
const metroLogText = metroLogAbs ? fs.readFileSync(metroLogAbs, 'utf8') : '';
|
|
159
|
+
const staleNativeModule = supersededErrorCapture(metroLogText, {
|
|
160
|
+
errorPattern: NATIVE_MODULE_STALE,
|
|
161
|
+
okPattern: BUNDLE_OK,
|
|
162
|
+
});
|
|
163
|
+
if (staleNativeModule) {
|
|
164
|
+
return {
|
|
165
|
+
schemaVersion: 1,
|
|
166
|
+
adapter: 'mobile',
|
|
167
|
+
target: resolved,
|
|
168
|
+
decision: 'launch',
|
|
169
|
+
reasonCode: 'native-module-stale',
|
|
170
|
+
reasons: [
|
|
171
|
+
`Installed dev client is missing native module "${staleNativeModule}" (JS deps are current). Rebuild the native app (yarn start:ios / yarn start:android or --preflight-mode rebuild-native).`,
|
|
172
|
+
'Metro may report a successful bundle while the installed dev client binary predates the native Nitro module link.',
|
|
173
|
+
],
|
|
174
|
+
checks,
|
|
175
|
+
actions: [
|
|
176
|
+
{ id: 'rebuild-native-dev-client' },
|
|
177
|
+
...launchActions(resolved, true),
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (metroLog.status === 'errors') {
|
|
183
|
+
const depsSatisfied = deps.status === 'current';
|
|
184
|
+
if (
|
|
185
|
+
depsSatisfied
|
|
186
|
+
&& (metroLog.reason === 'stale-bundle-error'
|
|
187
|
+
|| metroLog.reason === 'bundle-error')
|
|
188
|
+
) {
|
|
189
|
+
const excerpt = metroLog.excerpt ?? '';
|
|
190
|
+
const persistent = evaluatePersistentBundleError(resolved, excerpt);
|
|
191
|
+
if (persistent.blocked) {
|
|
192
|
+
return {
|
|
193
|
+
schemaVersion: 1,
|
|
194
|
+
adapter: 'mobile',
|
|
195
|
+
target: resolved,
|
|
196
|
+
decision: 'blocked',
|
|
197
|
+
reasonCode: 'bundle-error-persistent',
|
|
198
|
+
reasons: [
|
|
199
|
+
'Metro bundle keeps failing with the same error after a cache-cleared relaunch was already suggested; fix the bundle error in app code before retrying recipe up.',
|
|
200
|
+
...(excerpt ? [excerpt] : []),
|
|
201
|
+
],
|
|
202
|
+
checks,
|
|
203
|
+
actions: [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
schemaVersion: 1,
|
|
208
|
+
adapter: 'mobile',
|
|
209
|
+
target: resolved,
|
|
210
|
+
decision: 'launch',
|
|
211
|
+
reasonCode: metroLog.reason === 'stale-bundle-error' ? 'metro-stale-log' : 'bundle-errors-recoverable',
|
|
212
|
+
reasons: [
|
|
213
|
+
metroLog.reason === 'stale-bundle-error'
|
|
214
|
+
? 'Metro log shows prior bundle failures but cited modules are installed; restart Metro with a cleared cache.'
|
|
215
|
+
: 'Metro bundle is failing with current deps; restart Metro (clear cache) before relaunching the dev client.',
|
|
216
|
+
...(excerpt ? [excerpt] : []),
|
|
217
|
+
],
|
|
218
|
+
checks,
|
|
219
|
+
actions: launchActions(resolved, true),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
schemaVersion: 1,
|
|
224
|
+
adapter: 'mobile',
|
|
225
|
+
target: resolved,
|
|
226
|
+
decision: 'install',
|
|
227
|
+
reasonCode: metroLog.reason === 'unresolved-module' ? 'deps-unresolved-module' : 'bundle-errors',
|
|
228
|
+
reasons: [
|
|
229
|
+
metroLog.reason === 'unresolved-module'
|
|
230
|
+
? 'Metro cannot resolve modules that are absent from node_modules; run yarn setup.'
|
|
231
|
+
: 'Metro bundle is failing; reinstall deps or clear Metro cache before retrying.',
|
|
232
|
+
...(metroLog.excerpt ? [metroLog.excerpt] : []),
|
|
233
|
+
],
|
|
234
|
+
checks,
|
|
235
|
+
actions: installActions(resolved),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (metro.status === 'down') {
|
|
240
|
+
return {
|
|
241
|
+
schemaVersion: 1,
|
|
242
|
+
adapter: 'mobile',
|
|
243
|
+
target: resolved,
|
|
244
|
+
decision: 'launch',
|
|
245
|
+
reasonCode: 'metro-down',
|
|
246
|
+
reasons: ['Metro is not reachable; start Metro and launch the dev client.'],
|
|
247
|
+
checks,
|
|
248
|
+
actions: launchActions(resolved),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (metroLog.status === 'bundling' || metroLog.status === 'no-log') {
|
|
253
|
+
return {
|
|
254
|
+
schemaVersion: 1,
|
|
255
|
+
adapter: 'mobile',
|
|
256
|
+
target: resolved,
|
|
257
|
+
decision: 'launch',
|
|
258
|
+
reasonCode: metroLog.status === 'bundling' ? 'bundle-in-progress' : 'runtime-cold',
|
|
259
|
+
reasons: [
|
|
260
|
+
metroLog.status === 'bundling'
|
|
261
|
+
? 'Metro bundle is still in progress; launch or wait for a successful bundle.'
|
|
262
|
+
: 'No successful Metro bundle recorded; start Metro and launch the dev client.',
|
|
263
|
+
],
|
|
264
|
+
checks,
|
|
265
|
+
actions: launchActions(resolved),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (metro.status === 'up' && metroLog.status === 'ok') {
|
|
270
|
+
return {
|
|
271
|
+
schemaVersion: 1,
|
|
272
|
+
adapter: 'mobile',
|
|
273
|
+
target: resolved,
|
|
274
|
+
decision: 'ready',
|
|
275
|
+
reasonCode: 'healthy',
|
|
276
|
+
reasons: ['Dependencies are current and Metro reports a successful bundle. Verify bridge before replay.'],
|
|
277
|
+
checks,
|
|
278
|
+
actions: [],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
schemaVersion: 1,
|
|
284
|
+
adapter: 'mobile',
|
|
285
|
+
target: resolved,
|
|
286
|
+
decision: 'launch',
|
|
287
|
+
reasonCode: 'runtime-unverified',
|
|
288
|
+
reasons: ['Dependencies are current; start or refresh Metro + dev client before replay.'],
|
|
289
|
+
checks,
|
|
290
|
+
actions: launchActions(resolved),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
4
|
+
while [ -L "$SOURCE" ]; do
|
|
5
|
+
SOURCE_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
|
|
6
|
+
TARGET="$(readlink "$SOURCE")"
|
|
7
|
+
if [[ "$TARGET" == /* ]]; then
|
|
8
|
+
SOURCE="$TARGET"
|
|
9
|
+
else
|
|
10
|
+
SOURCE="$SOURCE_DIR/$TARGET"
|
|
11
|
+
fi
|
|
12
|
+
done
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
|
|
14
|
+
# This script lives at <root>/orchestration/porcelain/; the runner root is two up.
|
|
15
|
+
RUNNER_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
16
|
+
|
|
17
|
+
ENSURE_DEPS="$RUNNER_DIR/orchestration/lib/ensure-runner-deps.sh"
|
|
18
|
+
if [ -f "$ENSURE_DEPS" ]; then
|
|
19
|
+
# shellcheck disable=SC1090
|
|
20
|
+
bash "$ENSURE_DEPS" "$RUNNER_DIR"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ "${1:-}" = "mobile" ]; then
|
|
24
|
+
shift
|
|
25
|
+
exec "$RUNNER_DIR/bin/mm-recipe" "$@"
|
|
26
|
+
fi
|
|
27
|
+
if [ "${1:-}" = "extension" ]; then
|
|
28
|
+
shift
|
|
29
|
+
exec "$RUNNER_DIR/bin/mme-recipe" "$@"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
find_protocol_root() {
|
|
33
|
+
local start="$1"
|
|
34
|
+
local dir
|
|
35
|
+
dir="$(cd "$start" && pwd -P)"
|
|
36
|
+
while [ "$dir" != "/" ]; do
|
|
37
|
+
if [ -f "$dir/packages/recipe-harness/package.json" ] && [ -f "$dir/packages/protocol/package.json" ]; then
|
|
38
|
+
printf '%s\n' "$dir"
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
if [ -f "$dir/farmslot/packages/recipe-harness/package.json" ] && [ -f "$dir/farmslot/packages/protocol/package.json" ]; then
|
|
42
|
+
printf '%s\n' "$dir/farmslot"
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
dir="$(dirname "$dir")"
|
|
46
|
+
done
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
node_can_run_source_typescript() {
|
|
51
|
+
# Node refuses type-stripping for files under node_modules (ERR_UNSUPPORTED_NODE_
|
|
52
|
+
# MODULES_TYPE_STRIPPING). When this runner is installed as a dependency, cli.ts
|
|
53
|
+
# lives under node_modules and cannot run under plain node — a /tmp probe would be a
|
|
54
|
+
# false positive. Skip the node-source path in that case and fall through to dist/tsx.
|
|
55
|
+
case "$RUNNER_DIR/" in
|
|
56
|
+
*/node_modules/*) return 1 ;;
|
|
57
|
+
esac
|
|
58
|
+
local probe_dir probe_file
|
|
59
|
+
probe_dir="$(mktemp -d "${TMPDIR:-/tmp}/metamask-recipe-node-ts.XXXXXX")"
|
|
60
|
+
probe_file="$probe_dir/probe.ts"
|
|
61
|
+
printf 'const answer: number = 42; if (answer !== 42) process.exit(1);\n' > "$probe_file"
|
|
62
|
+
if ! node --no-warnings "$probe_file" >/dev/null 2>&1; then
|
|
63
|
+
rm -rf "$probe_dir"
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
rm -rf "$probe_dir"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if [ -f "$RUNNER_DIR/dist/cli.js" ]; then
|
|
70
|
+
exec node "$RUNNER_DIR/dist/cli.js" "$@"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
TSX_BIN="${TSX_BIN:-$RUNNER_DIR/node_modules/.bin/tsx}"
|
|
74
|
+
if [ ! -x "$TSX_BIN" ] && [ -x "$RUNNER_DIR/../../.bin/tsx" ]; then
|
|
75
|
+
TSX_BIN="$RUNNER_DIR/../../.bin/tsx"
|
|
76
|
+
fi
|
|
77
|
+
if [ ! -x "$TSX_BIN" ]; then
|
|
78
|
+
if [ -z "${FARMSLOT_ROOT:-}" ] && [ -f "$RUNNER_DIR/.farmslot-root" ]; then
|
|
79
|
+
FARMSLOT_ROOT="$(cat "$RUNNER_DIR/.farmslot-root")"
|
|
80
|
+
fi
|
|
81
|
+
if [ -z "${FARMSLOT_ROOT:-}" ]; then
|
|
82
|
+
FARMSLOT_ROOT="$(find_protocol_root "$RUNNER_DIR" || find_protocol_root "$PWD" || true)"
|
|
83
|
+
fi
|
|
84
|
+
if [ -n "${FARMSLOT_ROOT:-}" ]; then
|
|
85
|
+
FARMSLOT_ROOT="$(cd "$FARMSLOT_ROOT" && pwd -P)"
|
|
86
|
+
TSX_BIN="$FARMSLOT_ROOT/node_modules/.bin/tsx"
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if node_can_run_source_typescript; then
|
|
91
|
+
exec node --no-warnings "$RUNNER_DIR/runner/src/cli.ts" "$@"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if [ -x "$TSX_BIN" ]; then
|
|
95
|
+
exec "$TSX_BIN" "$RUNNER_DIR/runner/src/cli.ts" "$@"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "metamask-recipe source checkout requires dist/cli.js, Node.js TypeScript type stripping, or a local tsx binary. Install dependencies normally, or set TSX_BIN/FARMSLOT_ROOT for local protocol co-development." >&2
|
|
99
|
+
exit 1
|