@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,175 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# launch.sh — extension instance launch: prepare command + CDP readiness
|
|
3
|
+
#
|
|
4
|
+
# Purpose:
|
|
5
|
+
# Runs the caller-supplied prepare command (cwd = target) for one slot,
|
|
6
|
+
# then confirms the app-control runtime via extension-readiness.
|
|
7
|
+
#
|
|
8
|
+
# Inputs (flags / env):
|
|
9
|
+
# --target <metamask-extension> (default $PWD)
|
|
10
|
+
# --cdp-port <port> (numeric-validated)
|
|
11
|
+
# --prepare-cmd <cmd> (env RECIPE_HARNESS_EXTENSION_LAUNCH_CMD)
|
|
12
|
+
# --artifacts-dir <dir> (default <harness>/launch/<UTC>)
|
|
13
|
+
#
|
|
14
|
+
# Outputs:
|
|
15
|
+
# <artifacts>/summary.json (adapter/action/status/prepare/appControl),
|
|
16
|
+
# <artifacts>/logs/{launch.log,extension-readiness.json}.
|
|
17
|
+
# Exit 0 — launch pass; 1 — harness not installed / prepare or readiness
|
|
18
|
+
# failed; 2 — bad args.
|
|
19
|
+
#
|
|
20
|
+
# Never touches: product source files; anything outside the target's
|
|
21
|
+
# harness dir and the artifacts dir.
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
TARGET="$PWD"
|
|
25
|
+
CDP_PORT=""
|
|
26
|
+
ARTIFACTS=""
|
|
27
|
+
PREPARE_CMD="${RECIPE_HARNESS_EXTENSION_LAUNCH_CMD:-}"
|
|
28
|
+
while [ "$#" -gt 0 ]; do
|
|
29
|
+
case "$1" in
|
|
30
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
31
|
+
--cdp-port) CDP_PORT="$2"; shift 2 ;;
|
|
32
|
+
--artifacts-dir) ARTIFACTS="$2"; shift 2 ;;
|
|
33
|
+
--prepare-cmd) PREPARE_CMD="$2"; shift 2 ;;
|
|
34
|
+
-h|--help) echo "Usage: launch.sh [--target <metamask-extension>] [--cdp-port <port>] [--prepare-cmd <cmd>] [--artifacts-dir <dir>]"; exit 0 ;;
|
|
35
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
# Reject a non-numeric --cdp-port before it reaches the prepare/launch command.
|
|
40
|
+
if [ -n "$CDP_PORT" ]; then
|
|
41
|
+
case "$CDP_PORT" in
|
|
42
|
+
*[!0-9]*) echo "Invalid --cdp-port (must be numeric): $CDP_PORT" >&2; exit 2 ;;
|
|
43
|
+
esac
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
47
|
+
# shellcheck disable=SC1091
|
|
48
|
+
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
49
|
+
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
50
|
+
done
|
|
51
|
+
unset _hp
|
|
52
|
+
if ! command -v harness_root >/dev/null 2>&1; then
|
|
53
|
+
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
READINESS_MJS="$SCRIPT_DIR/readiness.mjs"
|
|
57
|
+
if [ ! -f "$READINESS_MJS" ]; then
|
|
58
|
+
echo "mm-harness: readiness.mjs not found next to $SCRIPT_DIR; reinstall the runner." >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
62
|
+
HARNESS_DIR="$(harness_dir "$TARGET" extension)"
|
|
63
|
+
ARTIFACTS="${ARTIFACTS:-$HARNESS_DIR/launch/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
64
|
+
mkdir -p "$ARTIFACTS/logs"
|
|
65
|
+
|
|
66
|
+
if [ ! -f "$HARNESS_DIR/manifest.json" ]; then
|
|
67
|
+
echo "Extension recipe harness is not installed in $TARGET. Run metamask-recipe extension prepare --target $TARGET first." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
LIB_DIR=""
|
|
72
|
+
for _lib in "$SCRIPT_DIR/lib" "$SCRIPT_DIR/../lib"; do
|
|
73
|
+
[ -f "$_lib/log-tui.mjs" ] && LIB_DIR="$_lib" && break
|
|
74
|
+
done
|
|
75
|
+
unset _lib
|
|
76
|
+
|
|
77
|
+
if [ -n "$PREPARE_CMD" ]; then
|
|
78
|
+
launch_log="$ARTIFACTS/logs/launch.log"
|
|
79
|
+
: > "$launch_log"
|
|
80
|
+
echo "[recipe-harness] running prepare pipeline (watch/build, dist snapshot, browser, fixture seed)..." | tee "$launch_log"
|
|
81
|
+
watch_pid=""
|
|
82
|
+
if [ -n "$LIB_DIR" ] && [ "${RECIPE_LOG_UI:-compact}" != "full" ] && [ "${RECIPE_LOG_TUI_PARENT:-}" != "1" ]; then
|
|
83
|
+
node "$LIB_DIR/log-tui.mjs" watch \
|
|
84
|
+
--log "$launch_log" \
|
|
85
|
+
--events "${RECIPE_LOG_EVENTS:-10}" \
|
|
86
|
+
--mode "${RECIPE_LOG_UI:-compact}" \
|
|
87
|
+
--label "extension prepare" \
|
|
88
|
+
--success-pattern 'yarn start compiled|runtime-dist snapshot complete|Extension harness launch pass|phase 2/2' \
|
|
89
|
+
--poll-ms 1000 &
|
|
90
|
+
watch_pid=$!
|
|
91
|
+
fi
|
|
92
|
+
set +e
|
|
93
|
+
(
|
|
94
|
+
cd "$TARGET"
|
|
95
|
+
bash -lc "$PREPARE_CMD"
|
|
96
|
+
) 2>&1 | tee -a "$launch_log"
|
|
97
|
+
prepare_status=${PIPESTATUS[0]}
|
|
98
|
+
set -e
|
|
99
|
+
[ -n "$watch_pid" ] && kill "$watch_pid" >/dev/null 2>&1 && wait "$watch_pid" 2>/dev/null || true
|
|
100
|
+
else
|
|
101
|
+
echo "No Extension prepare command supplied; reusing existing CDP runtime if reachable." | tee "$ARTIFACTS/logs/launch.log"
|
|
102
|
+
prepare_status=0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
status="pass"
|
|
106
|
+
if [ "$prepare_status" -ne 0 ]; then
|
|
107
|
+
status="fail"
|
|
108
|
+
elif [ -z "$CDP_PORT" ]; then
|
|
109
|
+
echo "Missing --cdp-port; cannot confirm Extension app-control runtime." | tee -a "$ARTIFACTS/logs/launch.log"
|
|
110
|
+
status="fail"
|
|
111
|
+
elif node "$READINESS_MJS" --target "$TARGET" --cdp-port "$CDP_PORT" --json > "$ARTIFACTS/logs/extension-readiness.json" 2>&1; then
|
|
112
|
+
:
|
|
113
|
+
else
|
|
114
|
+
status="fail"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Live extension-console viewer tab (default-on): tail the running extension's
|
|
118
|
+
# service worker + UI-page console in a tmux window, mirroring the Metro/webpack
|
|
119
|
+
# tabs. Skipped silently when there is no tmux session or CDP (headless/CI), so
|
|
120
|
+
# it never affects the launch result.
|
|
121
|
+
if [ "$status" = "pass" ] && [ -n "$CDP_PORT" ] && command -v tmux >/dev/null 2>&1; then
|
|
122
|
+
console_session="${RECIPE_TMUX_SESSION:-}"
|
|
123
|
+
if [ -z "$console_session" ] && [[ "$(basename "$TARGET")" =~ -([0-9]+)$ ]]; then
|
|
124
|
+
console_session="mme-${BASH_REMATCH[1]}"
|
|
125
|
+
fi
|
|
126
|
+
console_tail="$SCRIPT_DIR/console-tail.mjs"
|
|
127
|
+
if [ -n "$console_session" ] && [ -f "$console_tail" ] && tmux has-session -t "$console_session" 2>/dev/null; then
|
|
128
|
+
console_runtime="$TARGET/$( (cd "$TARGET" && recipe_runtime_dir) 2>/dev/null || echo temp/recipe/runtime)"
|
|
129
|
+
mkdir -p "$console_runtime"
|
|
130
|
+
console_log="$console_runtime/extension-console.log"
|
|
131
|
+
: > "$console_log"
|
|
132
|
+
console_window="console-${CDP_PORT}"
|
|
133
|
+
tmux kill-window -t "${console_session}:${console_window}" >/dev/null 2>&1 || true
|
|
134
|
+
tmux new-window -d -t "$console_session" -n "$console_window" \
|
|
135
|
+
"exec node $(printf '%q' "$console_tail") --cdp-port ${CDP_PORT} --log $(printf '%q' "$console_log")"
|
|
136
|
+
echo "[recipe-harness] extension console viewer in tmux window ${console_session}:${console_window}"
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" STATUS_FOR_SUMMARY="$status" CDP_PORT_FOR_SUMMARY="$CDP_PORT" PREPARE_SUPPLIED="$([ -n "$PREPARE_CMD" ] && echo true || echo false)" PREPARE_STATUS="$prepare_status" node <<'NODE'
|
|
141
|
+
const fs = require('fs');
|
|
142
|
+
const path = require('path');
|
|
143
|
+
const target = process.env.TARGET_FOR_SUMMARY;
|
|
144
|
+
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
145
|
+
let readiness = null;
|
|
146
|
+
try { readiness = JSON.parse(fs.readFileSync(path.join(artifacts, 'logs/extension-readiness.json'), 'utf8')); } catch {}
|
|
147
|
+
const appControlStatus =
|
|
148
|
+
process.env.STATUS_FOR_SUMMARY === 'pass' && readiness && readiness.status !== 'fail' ? 'pass' : 'fail';
|
|
149
|
+
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
150
|
+
adapter: 'extension',
|
|
151
|
+
action: 'launch',
|
|
152
|
+
status: process.env.STATUS_FOR_SUMMARY,
|
|
153
|
+
target,
|
|
154
|
+
cdpPort: process.env.CDP_PORT_FOR_SUMMARY || null,
|
|
155
|
+
prepare: {
|
|
156
|
+
commandSupplied: process.env.PREPARE_SUPPLIED === 'true',
|
|
157
|
+
status: Number(process.env.PREPARE_STATUS) === 0 ? 'pass' : 'fail',
|
|
158
|
+
exitCode: Number(process.env.PREPARE_STATUS),
|
|
159
|
+
logPath: path.join(artifacts, 'logs/launch.log'),
|
|
160
|
+
},
|
|
161
|
+
runtimePolicy: {
|
|
162
|
+
runtimeReusePolicy: 'reuse a running harness-compatible CDP target when possible; caller-supplied startup commands must use cached/watch-only paths unless the human explicitly permits a rebuild',
|
|
163
|
+
},
|
|
164
|
+
appControl: {
|
|
165
|
+
status: appControlStatus,
|
|
166
|
+
readiness,
|
|
167
|
+
},
|
|
168
|
+
cleanupCommand: 'Use the installed harness manifest cleanupCommand.',
|
|
169
|
+
note: 'Launch starts/reuses the harness runtime only; it does not run a recipe or claim evidence validation. Extension startup commands are caller-supplied so the runner does not encode local machine aliases.',
|
|
170
|
+
generatedAt: new Date().toISOString(),
|
|
171
|
+
}, null, 2)}\n`);
|
|
172
|
+
NODE
|
|
173
|
+
|
|
174
|
+
echo "Extension harness launch $status: $ARTIFACTS/summary.json"
|
|
175
|
+
[ "$status" = "pass" ]
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# live.sh — extension launch-then-verify sequencer for one live slot run
|
|
3
|
+
# mega-string now decomposed into start-watch.sh / snapshot-dist.sh /
|
|
4
|
+
# seed-fixture.sh)
|
|
5
|
+
#
|
|
6
|
+
# Purpose:
|
|
7
|
+
# Builds the per-run prepare command from the named feature scripts
|
|
8
|
+
# (watch, dist snapshot, fixture seed, detached chrome, CDP poll), then
|
|
9
|
+
# runs launch.sh and verify.sh under a timestamped artifact dir.
|
|
10
|
+
#
|
|
11
|
+
# Inputs (flags / env):
|
|
12
|
+
# --target <metamask-extension> (default $PWD)
|
|
13
|
+
# --cdp-port <port> (required, numeric-validated)
|
|
14
|
+
# --launch-existing-dist | --start-watch | --prepare-cmd <cmd>
|
|
15
|
+
# (env RECIPE_HARNESS_EXTENSION_LAUNCH_CMD)
|
|
16
|
+
# --dist-dir <rel> (default dist/chrome), --chrome-user-data-dir <dir>,
|
|
17
|
+
# --remote-flag <KEY=VARIANT[,KEY=VARIANT...]> (pins manifest _flags into the
|
|
18
|
+
# ephemeral runtime-dist snapshot before launch; omit for default behavior)
|
|
19
|
+
# --out <recipes-dir>, --artifacts-dir <dir>
|
|
20
|
+
# env RECIPE_HARNESS_CHROME_BIN (validated executable; else Playwright
|
|
21
|
+
# chromium with approval-gated install messaging), RECIPE_WALLET_FIXTURE,
|
|
22
|
+
# RECIPE_HARNESS_LIVE_KEEP (artifact pruning, default 5)
|
|
23
|
+
#
|
|
24
|
+
# Outputs:
|
|
25
|
+
# <artifacts>/summary.json (adapter/action/status, launch/verify exit
|
|
26
|
+
# codes + child summary paths, easyCommand), <artifacts>/{launch,verify},
|
|
27
|
+
# <artifacts>/logs/fixture-source.json provenance.
|
|
28
|
+
# Exit 0 — launch and verify both passed; 1 — either failed; 2 — bad args.
|
|
29
|
+
#
|
|
30
|
+
# Never touches: product source files; live artifact dirs newer than the
|
|
31
|
+
# pruning window; anything outside the target's harness dir.
|
|
32
|
+
set -euo pipefail
|
|
33
|
+
|
|
34
|
+
TARGET="$PWD"
|
|
35
|
+
CDP_PORT=""
|
|
36
|
+
ARTIFACTS=""
|
|
37
|
+
OUT=""
|
|
38
|
+
PREPARE_CMD="${RECIPE_HARNESS_EXTENSION_LAUNCH_CMD:-}"
|
|
39
|
+
LAUNCH_EXISTING_DIST=false
|
|
40
|
+
START_WATCH=false
|
|
41
|
+
DIST_DIR="dist/chrome"
|
|
42
|
+
CHROME_USER_DATA_DIR=""
|
|
43
|
+
REMOTE_FLAGS=""
|
|
44
|
+
REMOTE_FLAGS_APPLIED=false
|
|
45
|
+
while [ "$#" -gt 0 ]; do
|
|
46
|
+
case "$1" in
|
|
47
|
+
--target) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; TARGET="$2"; shift 2 ;;
|
|
48
|
+
--out) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; OUT="$2"; shift 2 ;;
|
|
49
|
+
--cdp-port) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; CDP_PORT="$2"; shift 2 ;;
|
|
50
|
+
--artifacts-dir) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; ARTIFACTS="$2"; shift 2 ;;
|
|
51
|
+
--prepare-cmd) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; PREPARE_CMD="$2"; shift 2 ;;
|
|
52
|
+
--launch-existing-dist) LAUNCH_EXISTING_DIST=true; shift ;;
|
|
53
|
+
--start-watch|--start-test-watch) START_WATCH=true; LAUNCH_EXISTING_DIST=true; shift ;;
|
|
54
|
+
--dist-dir) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; DIST_DIR="$2"; shift 2 ;;
|
|
55
|
+
--chrome-user-data-dir) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; CHROME_USER_DATA_DIR="$2"; shift 2 ;;
|
|
56
|
+
--remote-flag) [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; REMOTE_FLAGS="$2"; shift 2 ;;
|
|
57
|
+
-h|--help) echo "Usage: live.sh [--target <metamask-extension>] [--out <recipes-dir>] --cdp-port <port> [--launch-existing-dist|--start-watch|--prepare-cmd <cmd>] [--dist-dir dist/chrome] [--remote-flag KEY=VARIANT] [--artifacts-dir <dir>]"; exit 0 ;;
|
|
58
|
+
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
[ -n "$CDP_PORT" ] || { echo "Missing --cdp-port for Extension live validation" >&2; exit 2; }
|
|
63
|
+
# Reject a non-numeric port before it is interpolated into the Chrome launch
|
|
64
|
+
# command and the CDP HTTP probes.
|
|
65
|
+
case "$CDP_PORT" in
|
|
66
|
+
*[!0-9]*) echo "Invalid --cdp-port (must be numeric): $CDP_PORT" >&2; exit 2 ;;
|
|
67
|
+
esac
|
|
68
|
+
|
|
69
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
70
|
+
# shellcheck disable=SC1091
|
|
71
|
+
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
72
|
+
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
73
|
+
done
|
|
74
|
+
unset _hp
|
|
75
|
+
if ! command -v harness_root >/dev/null 2>&1; then
|
|
76
|
+
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
# verify.sh is a recipe-tree feature: co-located in installed copies, under
|
|
80
|
+
# runner/extension in a repo checkout.
|
|
81
|
+
VERIFY_SH=""
|
|
82
|
+
for _v in "$SCRIPT_DIR/verify.sh" "$SCRIPT_DIR/../../runner/extension/verify.sh"; do
|
|
83
|
+
[ -f "$_v" ] && { VERIFY_SH="$_v"; break; }
|
|
84
|
+
done
|
|
85
|
+
unset _v
|
|
86
|
+
if [ -z "$VERIFY_SH" ]; then
|
|
87
|
+
echo "mm-harness: extension verify.sh not found next to $SCRIPT_DIR; reinstall the runner." >&2
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
91
|
+
RUNTIME_DIR="$(recipe_runtime_dir)"
|
|
92
|
+
# Runner bin (installed wrapper → source runner). Used by the watch prepare path
|
|
93
|
+
# to defer the cache-clear DECISION to `runtime-decision` (single source) and to
|
|
94
|
+
# record the deps/cache baseline after a confirmed-good build.
|
|
95
|
+
RUNNER_BIN="$(harness_dir "$TARGET" extension)/runner/bin/metamask-recipe"
|
|
96
|
+
OUT="${OUT:-$(harness_root)/extension/runner/recipes}"
|
|
97
|
+
ARTIFACTS="${ARTIFACTS:-$(harness_dir "$TARGET" extension)/live/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
98
|
+
mkdir -p "$ARTIFACTS/logs"
|
|
99
|
+
|
|
100
|
+
# Prune stale live artifact dirs (configurable harness root, not a hardcoded path)
|
|
101
|
+
# so old runtime-dist snapshots can't be loaded and don't accumulate to GBs.
|
|
102
|
+
# Keep the most recent few; override count with RECIPE_HARNESS_LIVE_KEEP.
|
|
103
|
+
LIVE_ROOT="$(harness_dir "$TARGET" extension)/live"
|
|
104
|
+
LIVE_KEEP="${RECIPE_HARNESS_LIVE_KEEP:-5}"
|
|
105
|
+
# Must be a positive integer: a non-numeric value would break the arithmetic and a
|
|
106
|
+
# 0 would `tail -n +1` and delete every dir including this run's fresh $ARTIFACTS.
|
|
107
|
+
case "$LIVE_KEEP" in ''|*[!0-9]*) LIVE_KEEP=5 ;; esac
|
|
108
|
+
[ "$LIVE_KEEP" -ge 1 ] 2>/dev/null || LIVE_KEEP=5
|
|
109
|
+
if [ -d "$LIVE_ROOT" ]; then
|
|
110
|
+
# `|| true`: an empty live/ (no child dirs) makes ls exit non-zero, which would
|
|
111
|
+
# abort the script under `set -euo pipefail`. Pruning is best-effort.
|
|
112
|
+
ls -1dt "$LIVE_ROOT"/*/ 2>/dev/null | tail -n "+$((LIVE_KEEP + 1))" | while IFS= read -r _old; do rm -rf "$_old"; done || true
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if $LAUNCH_EXISTING_DIST && [ -z "$PREPARE_CMD" ]; then
|
|
116
|
+
DIST_ABS="$TARGET/$DIST_DIR"
|
|
117
|
+
RUNTIME_DIST_ABS="$ARTIFACTS/runtime-dist"
|
|
118
|
+
PROFILE_ABS="${CHROME_USER_DATA_DIR:-$ARTIFACTS/chrome-profile}"
|
|
119
|
+
FIXTURE_STATE_ABS="$ARTIFACTS/fixture-state.json"
|
|
120
|
+
FIXTURE_VALIDATION_ABS="$ARTIFACTS/logs/fixture-account-parity.json"
|
|
121
|
+
# Wallet fixture resolution chain + provenance live in seed-fixture.sh.
|
|
122
|
+
WALLET_FIXTURE_ABS="$(bash "$SCRIPT_DIR/seed-fixture.sh" resolve --target "$TARGET" --cdp-port "$CDP_PORT" --source-out "$ARTIFACTS/logs/fixture-source.json")"
|
|
123
|
+
if [ ! -f "$ARTIFACTS/logs/fixture-source.json" ]; then
|
|
124
|
+
# resolve failed before writing provenance (invalid RECIPE_WALLET_FIXTURE);
|
|
125
|
+
# keep the documented missing-provenance artifact.
|
|
126
|
+
node - "$ARTIFACTS/logs/fixture-source.json" "" <<'NODE'
|
|
127
|
+
const fs = require('fs');
|
|
128
|
+
const [out, fixture] = process.argv.slice(2);
|
|
129
|
+
fs.mkdirSync(require('path').dirname(out), { recursive: true });
|
|
130
|
+
fs.writeFileSync(out, `${JSON.stringify({
|
|
131
|
+
status: fixture ? 'present' : 'missing',
|
|
132
|
+
fixturePath: fixture || null,
|
|
133
|
+
generatedAt: new Date().toISOString(),
|
|
134
|
+
}, null, 2)}\n`);
|
|
135
|
+
NODE
|
|
136
|
+
fi
|
|
137
|
+
mkdir -p "$PROFILE_ABS"
|
|
138
|
+
quoted_dist="$(printf '%q' "$DIST_ABS")"
|
|
139
|
+
quoted_runtime_dist="$(printf '%q' "$RUNTIME_DIST_ABS")"
|
|
140
|
+
quoted_profile="$(printf '%q' "$PROFILE_ABS")"
|
|
141
|
+
quoted_seed_fixture="$(printf '%q' "$SCRIPT_DIR/seed-fixture.sh")"
|
|
142
|
+
quoted_start_watch="$(printf '%q' "$SCRIPT_DIR/start-watch.sh")"
|
|
143
|
+
quoted_snapshot_dist="$(printf '%q' "$SCRIPT_DIR/snapshot-dist.sh")"
|
|
144
|
+
quoted_chrome_launcher="$(printf '%q' "$SCRIPT_DIR/launch-browser.cjs")"
|
|
145
|
+
quoted_fixture_state="$(printf '%q' "$FIXTURE_STATE_ABS")"
|
|
146
|
+
quoted_fixture_validation="$(printf '%q' "$FIXTURE_VALIDATION_ABS")"
|
|
147
|
+
quoted_extension_id_file="$(printf '%q' "$TARGET/$RUNTIME_DIR/extension.id")"
|
|
148
|
+
quoted_target="$(printf '%q' "$TARGET")"
|
|
149
|
+
quoted_runner="$(printf '%q' "$RUNNER_BIN")"
|
|
150
|
+
quoted_runtime_dir="$(printf '%q' "$RUNTIME_DIR")"
|
|
151
|
+
if [ -n "${RECIPE_HARNESS_CHROME_BIN:-}" ]; then
|
|
152
|
+
CHROME_BIN="$RECIPE_HARNESS_CHROME_BIN"
|
|
153
|
+
if [ ! -f "$CHROME_BIN" ] || [ ! -x "$CHROME_BIN" ]; then
|
|
154
|
+
echo "[recipe-harness] RECIPE_HARNESS_CHROME_BIN is not an executable file: $CHROME_BIN" >&2
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
if [ -z "${CHROME_BIN:-}" ]; then
|
|
160
|
+
# bash 3.2 (macOS /bin/bash) mis-parses heredocs inside $(...): its scanner
|
|
161
|
+
# trips on quotes/backticks in the JS. Write the probe to a temp file and
|
|
162
|
+
# command-substitute a plain `node` invocation instead.
|
|
163
|
+
_chrome_probe="$(mktemp -t chrome-probe-XXXXXX)"
|
|
164
|
+
cat > "$_chrome_probe" <<'NODE'
|
|
165
|
+
const fs = require('fs');
|
|
166
|
+
const path = require('path');
|
|
167
|
+
const { createRequire } = require('module');
|
|
168
|
+
const requireFromTarget = createRequire(path.join(process.cwd(), 'package.json'));
|
|
169
|
+
|
|
170
|
+
let chromium = null;
|
|
171
|
+
function shellQuote(value) {
|
|
172
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
173
|
+
}
|
|
174
|
+
for (const pkg of ['@playwright/test', 'playwright']) {
|
|
175
|
+
try {
|
|
176
|
+
chromium = requireFromTarget(pkg).chromium;
|
|
177
|
+
if (chromium) break;
|
|
178
|
+
} catch (_error) {
|
|
179
|
+
// Optional Playwright package unavailable; try the next package name.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!chromium) {
|
|
183
|
+
console.error('[recipe-harness] Playwright is not available from this checkout; install dependencies first, or set RECIPE_HARNESS_CHROME_BIN to an explicitly approved browser.');
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let executable = '';
|
|
188
|
+
try {
|
|
189
|
+
executable = chromium.executablePath();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
const message = error && error.message ? error.message : String(error);
|
|
192
|
+
console.error(`[recipe-harness] Could not resolve Playwright Chromium executable: ${message}. Manual approval required before installing the Playwright Chromium browser cache (no package.json changes); ask the user before running yarn exec playwright install chromium.`);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
if (!fs.existsSync(executable)) {
|
|
196
|
+
console.error(`[recipe-harness] Playwright Chromium is not installed at ${executable}. Manual approval required before installing the Playwright Chromium browser cache (no package.json changes). Ask the user for approval; if they agree, run: cd ${shellQuote(process.cwd())} && yarn exec playwright install chromium`);
|
|
197
|
+
console.error('[recipe-harness] To use a browser that is already installed, set RECIPE_HARNESS_CHROME_BIN=/path/to/chrome explicitly.');
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
process.stdout.write(executable);
|
|
202
|
+
NODE
|
|
203
|
+
CHROME_PROBE_LOG="$ARTIFACTS/logs/chrome-probe.log"
|
|
204
|
+
CHROME_BIN="$(cd "$TARGET" && node "$_chrome_probe" 2>"$CHROME_PROBE_LOG" || true)"
|
|
205
|
+
rm -f "$_chrome_probe"
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
if [ -z "${CHROME_BIN:-}" ]; then
|
|
209
|
+
for candidate in \
|
|
210
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
|
|
211
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium" \
|
|
212
|
+
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary" \
|
|
213
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"; do
|
|
214
|
+
if [ -x "$candidate" ]; then
|
|
215
|
+
CHROME_BIN="$candidate"
|
|
216
|
+
break
|
|
217
|
+
fi
|
|
218
|
+
done
|
|
219
|
+
if [ -n "${CHROME_BIN:-}" ]; then
|
|
220
|
+
echo "[recipe-harness] WARN: Playwright Chromium unavailable; falling back to system browser: $CHROME_BIN" >&2
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
if [ -z "${CHROME_BIN:-}" ]; then
|
|
225
|
+
[ -n "${CHROME_PROBE_LOG:-}" ] && cat "$CHROME_PROBE_LOG" >&2 2>/dev/null || true
|
|
226
|
+
echo "[recipe-harness] No approved Chromium binary selected; stopping before live Extension launch." >&2
|
|
227
|
+
exit 1
|
|
228
|
+
fi
|
|
229
|
+
quoted_chrome="$(printf '%q' "$CHROME_BIN")"
|
|
230
|
+
quoted_chrome_log="$(printf '%q' "$ARTIFACTS/logs/chrome.log")"
|
|
231
|
+
quoted_chrome_pid="$(printf '%q' "$ARTIFACTS/logs/chrome.pid")"
|
|
232
|
+
prepare_parts=()
|
|
233
|
+
if $START_WATCH; then
|
|
234
|
+
prepare_parts+=("bash ${quoted_start_watch} --runtime-dir ${quoted_runtime_dir} --runner-bin ${quoted_runner}")
|
|
235
|
+
fi
|
|
236
|
+
prepare_parts+=("bash ${quoted_snapshot_dist} --dist ${quoted_dist} --runtime-dist ${quoted_runtime_dist}")
|
|
237
|
+
# Optional A/B feature-flag pinning: patch the ephemeral snapshot manifest so
|
|
238
|
+
# manifest._flags wins over the fetched ClientConfigApi value. No-op unless
|
|
239
|
+
# --remote-flag was passed; the snapshot is re-created each run, so no revert.
|
|
240
|
+
if [ -n "$REMOTE_FLAGS" ]; then
|
|
241
|
+
quoted_pin_flags="$(printf '%q' "$SCRIPT_DIR/pin-remote-flags.cjs")"
|
|
242
|
+
quoted_remote_flags="$(printf '%q' "$REMOTE_FLAGS")"
|
|
243
|
+
quoted_runtime_manifest="$(printf '%q' "$RUNTIME_DIST_ABS/manifest.json")"
|
|
244
|
+
prepare_parts+=("node ${quoted_pin_flags} ${quoted_runtime_manifest} ${quoted_remote_flags}")
|
|
245
|
+
REMOTE_FLAGS_APPLIED=true
|
|
246
|
+
fi
|
|
247
|
+
if [ -n "$WALLET_FIXTURE_ABS" ]; then
|
|
248
|
+
quoted_wallet_fixture="$(printf '%q' "$WALLET_FIXTURE_ABS")"
|
|
249
|
+
prepare_parts+=("bash ${quoted_seed_fixture} prefill --target ${quoted_target} --fixture ${quoted_wallet_fixture} --state ${quoted_fixture_state} --profile ${quoted_profile} --extension-dir ${quoted_runtime_dist} --extension-id-file ${quoted_extension_id_file}")
|
|
250
|
+
fi
|
|
251
|
+
chrome_launch_cmd="node ${quoted_chrome_launcher} --chrome-bin ${quoted_chrome} --profile ${quoted_profile} --cdp-port ${CDP_PORT} --extension-dir ${quoted_runtime_dist} --chrome-log ${quoted_chrome_log} --chrome-pid ${quoted_chrome_pid}"
|
|
252
|
+
prepare_parts+=("$chrome_launch_cmd")
|
|
253
|
+
prepare_parts+=("for i in {1..60}; do curl -fsS --max-time 1 http://127.0.0.1:${CDP_PORT}/json/version >/dev/null 2>&1 && break; sleep 1; done; curl -fsS --max-time 1 http://127.0.0.1:${CDP_PORT}/json/version >/dev/null")
|
|
254
|
+
if [ -n "$WALLET_FIXTURE_ABS" ]; then
|
|
255
|
+
prepare_parts+=("bash ${quoted_seed_fixture} seed-cdp --target ${quoted_target} --fixture ${quoted_wallet_fixture} --state ${quoted_fixture_state} --cdp-port ${CDP_PORT} --extension-dir ${quoted_runtime_dist} --extension-id-file ${quoted_extension_id_file} --out ${quoted_fixture_validation}")
|
|
256
|
+
fi
|
|
257
|
+
PREPARE_CMD="set -euo pipefail; $(IFS='; '; printf '%s' "${prepare_parts[*]}")"
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
if [ -n "$REMOTE_FLAGS" ] && [ "$REMOTE_FLAGS_APPLIED" != "true" ]; then
|
|
261
|
+
echo "[recipe-harness] WARN: --remote-flag '$REMOTE_FLAGS' had no effect: flag pinning only applies to the snapshot launch path (--launch-existing-dist/--start-watch without a custom --prepare-cmd)." >&2
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
echo "Extension live validation command:"
|
|
265
|
+
display_args=(mm-harness launch --verify --target "$TARGET")
|
|
266
|
+
$LAUNCH_EXISTING_DIST && display_args+=(--launch-existing-dist)
|
|
267
|
+
$START_WATCH && display_args+=(--start-watch)
|
|
268
|
+
[ -n "$REMOTE_FLAGS" ] && display_args+=(--remote-flag "$REMOTE_FLAGS")
|
|
269
|
+
printf ' '
|
|
270
|
+
printf '%q ' "${display_args[@]}"
|
|
271
|
+
printf '\n'
|
|
272
|
+
echo "Launch artifacts: $ARTIFACTS/launch"
|
|
273
|
+
echo "Verify artifacts: $ARTIFACTS/verify"
|
|
274
|
+
|
|
275
|
+
launch_args=(--target "$TARGET" --cdp-port "$CDP_PORT" --artifacts-dir "$ARTIFACTS/launch")
|
|
276
|
+
[ -n "$PREPARE_CMD" ] && launch_args+=(--prepare-cmd "$PREPARE_CMD")
|
|
277
|
+
|
|
278
|
+
echo "[recipe-harness] phase 1/2: launch (prepare + CDP readiness)" >&2
|
|
279
|
+
set +e
|
|
280
|
+
"$SCRIPT_DIR/launch.sh" "${launch_args[@]}"
|
|
281
|
+
launch_status=$?
|
|
282
|
+
set -e
|
|
283
|
+
|
|
284
|
+
verify_status=1
|
|
285
|
+
if [ "$launch_status" -eq 0 ]; then
|
|
286
|
+
echo "[recipe-harness] phase 2/2: live verify (readiness + smoke recipe)" >&2
|
|
287
|
+
set +e
|
|
288
|
+
"$VERIFY_SH" --target "$TARGET" --out "$OUT" --cdp-port "$CDP_PORT" --artifacts-dir "$ARTIFACTS/verify"
|
|
289
|
+
verify_status=$?
|
|
290
|
+
set -e
|
|
291
|
+
else
|
|
292
|
+
echo "Skipping Extension live verify because launch failed; see $ARTIFACTS/launch/summary.json" >&2
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" CDP_PORT_FOR_SUMMARY="$CDP_PORT" LAUNCH_STATUS="$launch_status" VERIFY_STATUS="$verify_status" LAUNCH_EXISTING_DIST="$LAUNCH_EXISTING_DIST" START_WATCH="$START_WATCH" node <<'NODE'
|
|
296
|
+
const fs = require('fs');
|
|
297
|
+
const path = require('path');
|
|
298
|
+
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
299
|
+
const launchSummary = path.join(artifacts, 'launch', 'summary.json');
|
|
300
|
+
const verifySummary = path.join(artifacts, 'verify', 'summary.json');
|
|
301
|
+
const launchStatus = Number(process.env.LAUNCH_STATUS);
|
|
302
|
+
const verifyStatus = Number(process.env.VERIFY_STATUS);
|
|
303
|
+
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
304
|
+
adapter: 'extension',
|
|
305
|
+
action: 'live',
|
|
306
|
+
status: launchStatus === 0 && verifyStatus === 0 ? 'pass' : 'fail',
|
|
307
|
+
target: process.env.TARGET_FOR_SUMMARY,
|
|
308
|
+
cdpPort: process.env.CDP_PORT_FOR_SUMMARY,
|
|
309
|
+
launchExistingDist: process.env.LAUNCH_EXISTING_DIST === 'true',
|
|
310
|
+
startWatch: process.env.START_WATCH === 'true',
|
|
311
|
+
launch: { exitCode: launchStatus, summaryPath: fs.existsSync(launchSummary) ? launchSummary : null },
|
|
312
|
+
verify: { exitCode: verifyStatus, summaryPath: fs.existsSync(verifySummary) ? verifySummary : null },
|
|
313
|
+
easyCommand: `mm-harness launch --verify --target <repo>`,
|
|
314
|
+
note: 'Runs launch then live verify so a developer can validate browser startup, CDP readiness, recipe bridge, screenshots/fallback classification, and sample recipes from one runner-owned command.',
|
|
315
|
+
generatedAt: new Date().toISOString(),
|
|
316
|
+
}, null, 2)}\n`);
|
|
317
|
+
NODE
|
|
318
|
+
|
|
319
|
+
echo "Extension live validation summary: $ARTIFACTS/summary.json"
|
|
320
|
+
[ "$launch_status" -eq 0 ] && [ "$verify_status" -eq 0 ]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// pin-remote-flags.cjs <manifest.json> <KEY=VARIANT[,KEY=VARIANT...]>
|
|
3
|
+
//
|
|
4
|
+
// Pins A/B remote feature-flag variants into an extension manifest's
|
|
5
|
+
// `_flags.remoteFeatureFlags`. MetaMask resolves the active flags as
|
|
6
|
+
// getRemoteFeatureFlags = merge({}, fetchedState, manifest._flags.remoteFeatureFlags)
|
|
7
|
+
// with the manifest winning, so this deterministically forces a variant in the
|
|
8
|
+
// live browser regardless of the real ClientConfigApi response — which otherwise
|
|
9
|
+
// overwrites any seeded controller state on refetch.
|
|
10
|
+
//
|
|
11
|
+
// Intended for the per-run runtime-dist SNAPSHOT (ephemeral, re-created from
|
|
12
|
+
// dist/chrome on every launch), so it never mutates the real dist/chrome build
|
|
13
|
+
// and needs no revert bookkeeping. Values are written as { name: VARIANT }
|
|
14
|
+
// (named-variant A/B flags); boolean/other flag shapes are out of scope.
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
|
|
19
|
+
const [, , manifestPath, spec] = process.argv;
|
|
20
|
+
if (!manifestPath || !spec) {
|
|
21
|
+
throw new Error('usage: pin-remote-flags.cjs <manifest.json> <KEY=VARIANT[,KEY=VARIANT...]>');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const pairs = spec
|
|
25
|
+
.split(',')
|
|
26
|
+
.map((entry) => entry.trim())
|
|
27
|
+
.filter(Boolean);
|
|
28
|
+
if (pairs.length === 0) throw new Error(`--remote-flag had no KEY=VARIANT pairs: ${spec}`);
|
|
29
|
+
|
|
30
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
31
|
+
manifest._flags = manifest._flags || {};
|
|
32
|
+
const flags = manifest._flags.remoteFeatureFlags || (manifest._flags.remoteFeatureFlags = {});
|
|
33
|
+
|
|
34
|
+
const applied = [];
|
|
35
|
+
for (const pair of pairs) {
|
|
36
|
+
const eq = pair.indexOf('=');
|
|
37
|
+
const key = eq > 0 ? pair.slice(0, eq) : '';
|
|
38
|
+
const variant = eq > 0 ? pair.slice(eq + 1) : '';
|
|
39
|
+
if (!key || !variant) throw new Error(`--remote-flag expects KEY=VARIANT, got: ${pair}`);
|
|
40
|
+
flags[key] = { name: variant };
|
|
41
|
+
applied.push(`${key}=${variant}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
45
|
+
console.log(`[flags] Pinned ${applied.join(', ')} into ${manifestPath}`);
|