@deeeed/metamask-harness 0.2.0 → 0.3.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 +17 -0
- package/README.md +20 -17
- package/{orchestration → adapters}/core/cleanup.sh +2 -2
- package/{orchestration → adapters}/core/inject.sh +8 -8
- package/{orchestration → adapters}/extension/cleanup.mjs +1 -1
- package/{orchestration → adapters}/extension/console-tail.mjs +3 -0
- package/{orchestration → adapters}/extension/ensure-browser.sh +4 -4
- package/{orchestration → adapters}/extension/inject.mjs +27 -31
- package/{orchestration → adapters}/extension/launch.sh +4 -4
- package/{orchestration → adapters}/extension/live.sh +5 -5
- package/{orchestration → adapters}/extension/readiness.mjs +12 -2
- package/{orchestration → adapters}/extension/refresh-build.sh +1 -1
- package/{orchestration → adapters}/extension/seed-fixture.sh +1 -1
- package/{orchestration → adapters}/extension/start-watch.sh +7 -7
- package/{runner → adapters}/extension/verify.sh +11 -11
- package/{orchestration → adapters}/extension/wallet-fixture-state.cjs +2 -2
- package/{orchestration → adapters}/manifest.json +96 -96
- package/{library/actions → adapters}/mobile/bridge-runtime/cdp-bridge.cjs +5 -5
- package/{orchestration → adapters}/mobile/cleanup.sh +3 -3
- package/{orchestration → adapters}/mobile/inject.sh +22 -24
- package/adapters/mobile/open-device.sh +250 -0
- package/{orchestration/compat-overlays → adapters/mobile/overlay/compat}/README.md +13 -4
- package/adapters/mobile/prewarm-bundle.sh +95 -0
- package/adapters/mobile/start-metro.sh +228 -0
- package/{runner → adapters}/mobile/verify.sh +6 -6
- package/adapters/mobile/wait-for-bridge.sh +108 -0
- package/adapters/mobile/yarn-setup.sh +20 -0
- package/{orchestration/lib → adapters/shared}/cli-ux.sh +5 -9
- package/{orchestration/lib → adapters/shared}/log-tui.mjs +57 -1
- package/{orchestration/lib → adapters/shared}/open-debug.mjs +32 -10
- package/{orchestration/lib → adapters/shared}/resolve-farmslot-ports.sh +47 -0
- package/adapters/shared/sync-wallet-fixture.sh +78 -0
- package/bin/mm-harness +5 -6
- package/docs/CLI-SPEC.md +41 -2
- package/docs/architecture.md +69 -38
- package/docs/runtime-file-conventions.md +3 -3
- package/library/actions/core/perps/_controller.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +2 -2
- package/library/actions/extension/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +1 -1
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/mobile/wallet/setup.mjs +2 -2
- package/library/library.json +1 -1
- package/package.json +8 -8
- package/{orchestration → src/adapters}/extension/runtime-decision.ts +5 -5
- package/{orchestration → src/adapters}/extension/runtime.ts +1 -1
- package/src/adapters/mobile/prepare.ts +210 -0
- package/{orchestration → src/adapters}/mobile/runtime-decision.ts +11 -3
- package/{runner/src → src}/adapters.ts +3 -3
- package/{orchestration/lib/cli-color.mjs → src/cli-color.ts} +21 -13
- package/{orchestration/lib/cli-commands.mjs → src/cli-commands.ts} +52 -45
- package/{orchestration/lib/cli-version.mjs → src/cli-version.ts} +35 -17
- package/{runner/src → src}/cli.ts +25 -18
- package/src/commands/debug.ts +61 -0
- package/src/commands/fixtures.ts +196 -0
- package/{runner/src → src}/commands/launch.ts +185 -41
- package/src/commands/logs.ts +86 -0
- package/{runner/src → src}/commands/shared.ts +26 -26
- package/{runner/src → src}/harness.ts +97 -10
- package/src/index.ts +15 -0
- package/{runner/src → src}/live-adapter-contract.ts +1 -1
- package/{runner/src → src}/mm-harness-cli.ts +19 -3
- package/{runner/src → src}/paths.ts +2 -2
- package/{orchestration/lib/progress.mjs → src/progress.ts} +31 -21
- package/docs/extension-runtime-commands.md +0 -60
- package/docs/harness-cli.md +0 -43
- package/orchestration/compat-overlays/mobile/README.md +0 -13
- package/orchestration/doctor.mjs +0 -72
- package/orchestration/lib/cli-home.mjs +0 -354
- package/orchestration/lib/recipe-paths.mjs +0 -26
- package/orchestration/mobile/launch.sh +0 -137
- package/orchestration/mobile/live.sh +0 -125
- package/orchestration/porcelain/metamask-recipe +0 -99
- package/orchestration/porcelain/mm-recipe +0 -1591
- package/orchestration/porcelain/mme-recipe +0 -1181
- package/runner/src/commands/debug.ts +0 -44
- package/runner/src/commands/fixtures.ts +0 -99
- package/runner/src/commands/logs.ts +0 -60
- package/runner/src/index.ts +0 -6
- /package/{orchestration → adapters}/extension/launch-browser.cjs +0 -0
- /package/{orchestration → adapters}/extension/pin-remote-flags.cjs +0 -0
- /package/{orchestration → adapters}/extension/sidepanel-toggle.sh +0 -0
- /package/{orchestration → adapters}/extension/snapshot-dist.sh +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/config.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/issue-capture.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/target-discovery.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/ws-client.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/setup-wallet.sh +0 -0
- /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -0
- /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -0
- /package/{orchestration/compat-overlays/mobile → adapters/mobile/overlay/compat}/rn81-message-event-source.patch +0 -0
- /package/{orchestration/lib → adapters/shared}/activate-repo-node.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/ensure-runner-deps.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/harness-path.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/hash-helpers.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/json-field.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/path-defaults.json +0 -0
- /package/{orchestration → src/adapters}/extension/ensure-ready.ts +0 -0
- /package/{orchestration → src/adapters}/extension/extension-id.ts +0 -0
- /package/{orchestration → src/adapters}/mobile/deps-markers.ts +0 -0
- /package/{runner/src → src}/completions-cache.ts +0 -0
- /package/{runner/src → src}/doctor.ts +0 -0
- /package/{runner/src → src}/heal-bounds.ts +0 -0
- /package/{runner/src → src}/manifest.ts +0 -0
- /package/{runner/src → src}/recording-target.ts +0 -0
- /package/{runner/src → src}/run-recording.ts +0 -0
- /package/{runner/src → src}/runner.ts +0 -0
- /package/{runner/src → src}/types.ts +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wait-for-bridge — poll the CDP bridge until a route target is registered.
|
|
3
|
+
#
|
|
4
|
+
# Calls cdp-bridge.cjs status in a poll loop. Emits structured diagnostics on
|
|
5
|
+
# each probe failure: Metro reachability, React Native target visibility, bundle
|
|
6
|
+
# progress. Fails with non-zero if the bridge is not ready within the poll limit.
|
|
7
|
+
#
|
|
8
|
+
# Inputs:
|
|
9
|
+
# --target <metamask-mobile dir> (default $PWD)
|
|
10
|
+
# --port <number> (default WATCHER_PORT env, else METRO_PORT, else 8081)
|
|
11
|
+
# --max-polls <n> (default MOBILE_BRIDGE_READY_POLLS env, else 90; 2s each = 180s)
|
|
12
|
+
# Outputs:
|
|
13
|
+
# Progress on stderr; exit 0 bridge ready; 1 timeout.
|
|
14
|
+
#
|
|
15
|
+
# Single device op: wait for bridge readiness. Does not start Metro or open the dev client.
|
|
16
|
+
set -uo pipefail
|
|
17
|
+
|
|
18
|
+
TARGET="$PWD"
|
|
19
|
+
PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
|
|
20
|
+
MAX_POLLS="${MOBILE_BRIDGE_READY_POLLS:-90}"
|
|
21
|
+
|
|
22
|
+
while [ "$#" -gt 0 ]; do
|
|
23
|
+
case "$1" in
|
|
24
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
25
|
+
--port) PORT="$2"; shift 2 ;;
|
|
26
|
+
--max-polls) MAX_POLLS="$2"; shift 2 ;;
|
|
27
|
+
-h|--help)
|
|
28
|
+
printf 'Usage: wait-for-bridge.sh [--target <dir>] [--port <n>] [--max-polls <n>]\n'
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
*) printf 'wait-for-bridge: unknown arg: %s\n' "$1" >&2; exit 2 ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
36
|
+
LOG_DIR="$TARGET/temp/recipe/runtime"
|
|
37
|
+
mkdir -p "$LOG_DIR"
|
|
38
|
+
|
|
39
|
+
BRIDGE_CJS="$(dirname "$0")/bridge-runtime/cdp-bridge.cjs"
|
|
40
|
+
STATUS_LOG="$LOG_DIR/bridge-status.log"
|
|
41
|
+
METRO_LOG="$LOG_DIR/metro.log"
|
|
42
|
+
|
|
43
|
+
if [ ! -f "$BRIDGE_CJS" ]; then
|
|
44
|
+
printf 'wait-for-bridge: cdp-bridge.cjs not found at %s\n' "$BRIDGE_CJS" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
metro_ready() {
|
|
49
|
+
curl -sf --max-time 2 "http://localhost:${PORT}/status" 2>/dev/null | grep -q 'packager-status:running'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bundle_progress() {
|
|
53
|
+
local log="$1"
|
|
54
|
+
[ -f "$log" ] || return 0
|
|
55
|
+
local p
|
|
56
|
+
p="$(grep -E '^[[:space:]]*(iOS|Android).*index\.(js|tsx?).*%|Bundl(ed|ing)|Finished' "$log" 2>/dev/null | tail -1 | tr -d '\r' || true)"
|
|
57
|
+
[ -n "$p" ] && printf ' (%s)' "$p"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
bridge_wait_reason() {
|
|
61
|
+
if ! metro_ready; then
|
|
62
|
+
printf 'Metro not reachable on port %s' "$PORT"
|
|
63
|
+
return
|
|
64
|
+
fi
|
|
65
|
+
local targets
|
|
66
|
+
targets="$(curl -sf --max-time 2 "http://localhost:${PORT}/json/list" 2>/dev/null || true)"
|
|
67
|
+
if [ "$targets" = "[]" ] || [ -z "$targets" ]; then
|
|
68
|
+
printf 'Metro running; no React Native debug target yet'
|
|
69
|
+
bundle_progress "$METRO_LOG"
|
|
70
|
+
return
|
|
71
|
+
fi
|
|
72
|
+
if [ -s "$STATUS_LOG" ] && grep -qxF '[]' "$STATUS_LOG"; then
|
|
73
|
+
printf 'React Native target visible; in-app bridge not ready yet'
|
|
74
|
+
bundle_progress "$METRO_LOG"
|
|
75
|
+
return
|
|
76
|
+
fi
|
|
77
|
+
printf 'bridge probe failed'
|
|
78
|
+
bundle_progress "$METRO_LOG"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
bridge_has_route() {
|
|
82
|
+
(cd "$TARGET" && APP_ROOT="$TARGET" node "$BRIDGE_CJS" status > "$STATUS_LOG" 2>&1) \
|
|
83
|
+
&& node - "$STATUS_LOG" <<'NODE'
|
|
84
|
+
const fs = require('fs');
|
|
85
|
+
try {
|
|
86
|
+
const value = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
87
|
+
const targets = Array.isArray(value) ? value : [value];
|
|
88
|
+
if (!targets.some((t) => t && typeof t === 'object' && t.route)) process.exit(1);
|
|
89
|
+
} catch { process.exit(1); }
|
|
90
|
+
NODE
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for ATTEMPT in $(seq 1 "$MAX_POLLS"); do
|
|
94
|
+
if bridge_has_route; then
|
|
95
|
+
printf 'Mobile bridge ready\n' >&2
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
98
|
+
if [ "$ATTEMPT" = "1" ] || [ $((ATTEMPT % 5)) -eq 0 ]; then
|
|
99
|
+
printf 'Waiting for Mobile bridge (%s/%s): %s\n' "$ATTEMPT" "$MAX_POLLS" "$(bridge_wait_reason)" >&2
|
|
100
|
+
fi
|
|
101
|
+
sleep 2
|
|
102
|
+
done
|
|
103
|
+
|
|
104
|
+
cat "$STATUS_LOG" >&2 || true
|
|
105
|
+
printf 'wait-for-bridge: bridge did not become ready on port %s (%s polls × 2s = %ss)\n' \
|
|
106
|
+
"$PORT" "$MAX_POLLS" "$((MAX_POLLS * 2))" >&2
|
|
107
|
+
printf ' Next: check %s — or run mm-harness app-status\n' "$STATUS_LOG" >&2
|
|
108
|
+
exit 1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# yarn-setup — install node_modules for MetaMask Mobile.
|
|
3
|
+
#
|
|
4
|
+
# Inputs: --target <metamask-mobile dir> (default $PWD)
|
|
5
|
+
# Outputs: yarn install output on stderr; exit 0 pass, 1 install failed, 2 bad args.
|
|
6
|
+
#
|
|
7
|
+
# Single device op: install node_modules. No other sequencing.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
TARGET="$PWD"
|
|
11
|
+
while [ "$#" -gt 0 ]; do
|
|
12
|
+
case "$1" in
|
|
13
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
14
|
+
-h|--help) printf 'Usage: yarn-setup.sh [--target <dir>]\n'; exit 0 ;;
|
|
15
|
+
*) printf 'yarn-setup: unknown arg: %s\n' "$1" >&2; exit 2 ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
20
|
+
cd "$TARGET" && exec yarn setup
|
|
@@ -26,19 +26,15 @@ recipe_color() {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
recipe_progress_script() {
|
|
29
|
-
printf '%s' "${RECIPE_PROGRESS_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
recipe_home_script() {
|
|
33
|
-
printf '%s' "${RECIPE_HOME_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/orchestration/lib/cli-home.mjs}"
|
|
29
|
+
printf '%s' "${RECIPE_PROGRESS_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/src/progress.ts}"
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
recipe_log_tui_script() {
|
|
37
|
-
printf '%s' "${RECIPE_LOG_TUI_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/
|
|
33
|
+
printf '%s' "${RECIPE_LOG_TUI_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/adapters/shared/log-tui.mjs}"
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
recipe_version_script() {
|
|
41
|
-
printf '%s' "${RECIPE_VERSION_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/
|
|
37
|
+
printf '%s' "${RECIPE_VERSION_SCRIPT:-${RECIPE_RUNNER_ROOT:?}/src/cli-version.ts}"
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
recipe_log_ui_mode() {
|
|
@@ -79,7 +75,7 @@ recipe_instant_hint() {
|
|
|
79
75
|
|
|
80
76
|
recipe_progress() {
|
|
81
77
|
local verb="$1" phase="$2" message="$3" elapsed="${4:-}"
|
|
82
|
-
local args=(emit --
|
|
78
|
+
local args=(emit --command "$verb" --phase "$phase" --message "$message")
|
|
83
79
|
[ -n "$elapsed" ] && args+=(--elapsed-ms "$elapsed")
|
|
84
80
|
[ "${RECIPE_PROGRESS_JSON:-}" = "1" ] && args+=(--json)
|
|
85
81
|
node "$(recipe_progress_script)" "${args[@]}" || true
|
|
@@ -87,7 +83,7 @@ recipe_progress() {
|
|
|
87
83
|
|
|
88
84
|
recipe_done() {
|
|
89
85
|
local verb="$1" status="$2" message="$3" elapsed="${4:-}"
|
|
90
|
-
local args=(done --
|
|
86
|
+
local args=(done --command "$verb" --status "$status" --message "$message")
|
|
91
87
|
[ -n "$elapsed" ] && args+=(--elapsed-ms "$elapsed")
|
|
92
88
|
[ "${RECIPE_PROGRESS_JSON:-}" = "1" ] && args+=(--json)
|
|
93
89
|
node "$(recipe_progress_script)" "${args[@]}" || true
|
|
@@ -4,7 +4,63 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
|
-
|
|
7
|
+
// Inline color helpers — self-contained so both the runner copy and any
|
|
8
|
+
// installed copy work without an external module dependency.
|
|
9
|
+
const STYLES = {
|
|
10
|
+
reset: '\x1b[0m',
|
|
11
|
+
bold: '\x1b[1m',
|
|
12
|
+
dim: '\x1b[2m',
|
|
13
|
+
label: '\x1b[1;36m',
|
|
14
|
+
cmd: '\x1b[1m',
|
|
15
|
+
ok: '\x1b[1;32m',
|
|
16
|
+
warn: '\x1b[1;33m',
|
|
17
|
+
err: '\x1b[1;31m',
|
|
18
|
+
info: '\x1b[0;37m',
|
|
19
|
+
accent: '\x1b[1;35m',
|
|
20
|
+
path: '\x1b[2m',
|
|
21
|
+
comment: '\x1b[2m',
|
|
22
|
+
active: '\x1b[1m',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function colorEnabled(stream = process.stderr) {
|
|
26
|
+
if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0') {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (process.env.RECIPE_NO_COLOR === '1') return false;
|
|
30
|
+
if (process.env.RECIPE_COLOR === '1' || process.env.FORCE_COLOR === '1') return true;
|
|
31
|
+
return Boolean(stream?.isTTY);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function color(style, text, { stream = process.stderr } = {}) {
|
|
35
|
+
const value = String(text);
|
|
36
|
+
if (!colorEnabled(stream)) return value;
|
|
37
|
+
const code = STYLES[style];
|
|
38
|
+
if (!code) return value;
|
|
39
|
+
return `${code}${value}${STYLES.reset}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function classifyLogEvent(event) {
|
|
43
|
+
const text = String(event || '');
|
|
44
|
+
if (/Module build failed|^ERROR in |BUILD FAILED|compiled with [1-9][0-9]* error/iu.test(text)) {
|
|
45
|
+
return 'err';
|
|
46
|
+
}
|
|
47
|
+
if (/compiled successfully|compiled with [0-9]+ warning|PASS runtime-launch|launch pass|verify pass|build complete/iu.test(text)) {
|
|
48
|
+
return 'ok';
|
|
49
|
+
}
|
|
50
|
+
if (/phase [0-9]\/[0-9]|prepare pipeline|snapshot|fixture:|dist-freshness|build-health/iu.test(text)) {
|
|
51
|
+
return 'label';
|
|
52
|
+
}
|
|
53
|
+
if (/webpack [0-9]+%|Bundl(ed|ing)|(iOS|Android).*%/iu.test(text)) {
|
|
54
|
+
return 'warn';
|
|
55
|
+
}
|
|
56
|
+
return 'info';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function colorLogEvent(event, { latest = false, stream = process.stderr } = {}) {
|
|
60
|
+
const style = classifyLogEvent(event);
|
|
61
|
+
const painted = color(style === 'label' ? 'label' : style, event, { stream });
|
|
62
|
+
return latest ? color('active', painted, { stream }) : painted;
|
|
63
|
+
}
|
|
8
64
|
|
|
9
65
|
const DEFAULT_EVENTS = 10;
|
|
10
66
|
|
|
@@ -115,16 +115,34 @@ function openUrl(url) {
|
|
|
115
115
|
return null;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// CDP-eval fallback: invoke DevSettings native module directly via the bridge
|
|
119
|
+
// when the Metro HTTP endpoint is unreachable or returns a non-OK response.
|
|
120
|
+
// Used for both debug (openDebugger) and dev-menu (show).
|
|
121
|
+
async function tryDevSettingsCdpFallback(numericPort, normalizedAction) {
|
|
122
|
+
const bridgePath = new URL('cdp-bridge.cjs', import.meta.url).pathname;
|
|
123
|
+
const expression =
|
|
124
|
+
normalizedAction === 'dev-menu'
|
|
125
|
+
? 'NativeModules.DevSettings.show()'
|
|
126
|
+
: 'NativeModules.DevSettings.openDebugger()';
|
|
127
|
+
const result = spawnSync(process.execPath, [bridgePath, 'eval', expression], {
|
|
128
|
+
encoding: 'utf8',
|
|
129
|
+
env: { ...process.env, WATCHER_PORT: String(numericPort) },
|
|
130
|
+
timeout: 15000,
|
|
131
|
+
});
|
|
132
|
+
return result.status === 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
async function openMobileDebugger(port, action, shouldOpen) {
|
|
119
136
|
const normalizedAction = action === 'dev-menu' ? 'dev-menu' : 'debug';
|
|
120
|
-
const
|
|
137
|
+
const urlPath = normalizedAction === 'dev-menu' ? '/open-dev-menu' : '/open-debugger';
|
|
121
138
|
const numericPort = Number.parseInt(String(port), 10);
|
|
122
139
|
if (!Number.isInteger(numericPort) || numericPort <= 0) {
|
|
123
140
|
return { ok: false, adapter: 'mobile', action: normalizedAction, error: 'invalid-port', port };
|
|
124
141
|
}
|
|
125
142
|
|
|
143
|
+
let metroError = null;
|
|
126
144
|
try {
|
|
127
|
-
const opened = await fetchOk(`http://127.0.0.1:${numericPort}${
|
|
145
|
+
const opened = await fetchOk(`http://127.0.0.1:${numericPort}${urlPath}`, { method: 'POST' });
|
|
128
146
|
if (opened) {
|
|
129
147
|
return {
|
|
130
148
|
ok: true,
|
|
@@ -135,16 +153,21 @@ async function openMobileDebugger(port, action, shouldOpen) {
|
|
|
135
153
|
opened: shouldOpen,
|
|
136
154
|
};
|
|
137
155
|
}
|
|
156
|
+
metroError = 'metro-http-nok';
|
|
138
157
|
} catch (error) {
|
|
158
|
+
metroError = error instanceof Error ? error.message : String(error);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// CDP-eval fallback via DevSettings native module.
|
|
162
|
+
const cdpOk = await tryDevSettingsCdpFallback(numericPort, normalizedAction);
|
|
163
|
+
if (cdpOk) {
|
|
139
164
|
return {
|
|
140
|
-
ok:
|
|
165
|
+
ok: true,
|
|
141
166
|
adapter: 'mobile',
|
|
142
167
|
action: normalizedAction,
|
|
143
|
-
method: '
|
|
168
|
+
method: 'cdp-eval',
|
|
144
169
|
port: numericPort,
|
|
145
|
-
|
|
146
|
-
hint: 'Metro may be down or the debug target is not ready. Try recipe refresh, then recipe runtime-status.',
|
|
147
|
-
fallback: 'cdp-eval',
|
|
170
|
+
opened: shouldOpen,
|
|
148
171
|
};
|
|
149
172
|
}
|
|
150
173
|
|
|
@@ -154,9 +177,8 @@ async function openMobileDebugger(port, action, shouldOpen) {
|
|
|
154
177
|
action: normalizedAction,
|
|
155
178
|
method: 'metro-http',
|
|
156
179
|
port: numericPort,
|
|
157
|
-
error:
|
|
158
|
-
hint: 'Metro
|
|
159
|
-
fallback: 'cdp-eval',
|
|
180
|
+
error: metroError,
|
|
181
|
+
hint: 'Metro may be down or the debug target is not ready. Check metro status and try again.',
|
|
160
182
|
};
|
|
161
183
|
}
|
|
162
184
|
|
|
@@ -142,3 +142,50 @@ apply_resolved_extension_ports() {
|
|
|
142
142
|
esac
|
|
143
143
|
done <<< "$resolved"
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
# Mobile slot defaults: derive Metro port + iOS simulator name from directory suffix.
|
|
147
|
+
# Port formula: 8060 + N (slot 1 → 8061). iOS simulator: mm-N.
|
|
148
|
+
# Mirrors the extension convention (6660+N / 9010+N) for multi-slot mobile farms.
|
|
149
|
+
resolve_mobile_slot_defaults() {
|
|
150
|
+
local repo="$1" n
|
|
151
|
+
n="$(infer_extension_slot_suffix "$repo" || true)"
|
|
152
|
+
[ -n "$n" ] || return 1
|
|
153
|
+
printf 'WATCHER_PORT=%s\n' "$((8060 + n))"
|
|
154
|
+
printf 'IOS_SIMULATOR=mm-%s\n' "$n"
|
|
155
|
+
printf 'SLOT_ID=local-mobile-%s\n' "$n"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
resolve_mobile_runtime_ports() {
|
|
159
|
+
local repo="${1:-.}"
|
|
160
|
+
if resolve_farmslot_ports_by_repo "$repo"; then
|
|
161
|
+
return 0
|
|
162
|
+
fi
|
|
163
|
+
resolve_mobile_slot_defaults "$repo"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
apply_resolved_mobile_ports() {
|
|
167
|
+
local repo="${1:-.}" line key val from_pool=false resolved=""
|
|
168
|
+
if resolved="$(resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
|
|
169
|
+
from_pool=true
|
|
170
|
+
elif resolved="$(resolve_mobile_slot_defaults "$repo" 2>/dev/null)"; then
|
|
171
|
+
from_pool=false
|
|
172
|
+
else
|
|
173
|
+
return 0
|
|
174
|
+
fi
|
|
175
|
+
while IFS= read -r line; do
|
|
176
|
+
[ -n "$line" ] || continue
|
|
177
|
+
key="${line%%=*}"
|
|
178
|
+
val="${line#*=}"
|
|
179
|
+
case "$key" in
|
|
180
|
+
WATCHER_PORT)
|
|
181
|
+
if [ "$from_pool" = true ] || [ -z "${WATCHER_PORT:-}" ]; then WATCHER_PORT="$val"; fi
|
|
182
|
+
;;
|
|
183
|
+
IOS_SIMULATOR)
|
|
184
|
+
if [ "$from_pool" = true ] || [ -z "${IOS_SIMULATOR:-}" ]; then IOS_SIMULATOR="$val"; fi
|
|
185
|
+
;;
|
|
186
|
+
SLOT_ID)
|
|
187
|
+
if [ "$from_pool" = true ] || [ -z "${RECIPE_SLOT_ID:-}" ]; then RECIPE_SLOT_ID="$val"; fi
|
|
188
|
+
;;
|
|
189
|
+
esac
|
|
190
|
+
done <<< "$resolved"
|
|
191
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sync-wallet-fixture.sh — copy the wallet fixture to the canonical runtime slot.
|
|
3
|
+
#
|
|
4
|
+
# Purpose:
|
|
5
|
+
# After a successful overlay install, ensures the canonical wallet fixture is
|
|
6
|
+
# present in the checkout's runtime dir. Non-zero exit is informational (no
|
|
7
|
+
# fixture source found); the install already succeeded, so the overlay is fresh.
|
|
8
|
+
#
|
|
9
|
+
# Inputs (flags / env):
|
|
10
|
+
# --target <checkout> (default $PWD)
|
|
11
|
+
# --slot-id <id> (env FARMSLOT_SLOT_ID, SLOT_ID — farmslot sync source)
|
|
12
|
+
# --cdp-port <port> (env CDP_PORT — extension fixture seeding port)
|
|
13
|
+
#
|
|
14
|
+
# Outputs:
|
|
15
|
+
# Copies wallet-fixture.json to <target>/<RECIPE_RUNTIME_DIR>/wallet-fixture.json
|
|
16
|
+
# when a source is found. Exit 0 always (non-zero only when no source found;
|
|
17
|
+
# callers treat this as informational).
|
|
18
|
+
#
|
|
19
|
+
# OS-glue only: file copy and farmslot delegation; decision logic lives in
|
|
20
|
+
# src/commands/fixtures.ts.
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
|
|
23
|
+
TARGET="$PWD"
|
|
24
|
+
SLOT_ID="${FARMSLOT_SLOT_ID:-${SLOT_ID:-}}"
|
|
25
|
+
CDP_PORT="${CDP_PORT:-}"
|
|
26
|
+
|
|
27
|
+
require_value() { [ "$#" -ge 2 ] || { printf 'Missing value for %s\n' "$1" >&2; exit 2; }; }
|
|
28
|
+
|
|
29
|
+
while [ "$#" -gt 0 ]; do
|
|
30
|
+
case "$1" in
|
|
31
|
+
--target) require_value "$@"; TARGET="$2"; shift 2 ;;
|
|
32
|
+
--slot-id) require_value "$@"; SLOT_ID="$2"; shift 2 ;;
|
|
33
|
+
--cdp-port) require_value "$@"; CDP_PORT="$2"; shift 2 ;;
|
|
34
|
+
-h|--help)
|
|
35
|
+
printf 'Usage: sync-wallet-fixture.sh [--target <dir>] [--slot-id <id>] [--cdp-port <port>]\n'
|
|
36
|
+
exit 0
|
|
37
|
+
;;
|
|
38
|
+
*) printf 'Unknown arg: %s\n' "$1" >&2; exit 2 ;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
43
|
+
RUNTIME_DIR="${RECIPE_RUNTIME_DIR:-temp/recipe/runtime}"
|
|
44
|
+
CANONICAL="$TARGET/$RUNTIME_DIR/wallet-fixture.json"
|
|
45
|
+
|
|
46
|
+
mkdir -p "$(dirname "$CANONICAL")"
|
|
47
|
+
|
|
48
|
+
# Already present — nothing to do.
|
|
49
|
+
if [ -f "$CANONICAL" ]; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# RECIPE_WALLET_FIXTURE env points at a fixture directly.
|
|
54
|
+
if [ -n "${RECIPE_WALLET_FIXTURE:-}" ] && [ -f "${RECIPE_WALLET_FIXTURE}" ]; then
|
|
55
|
+
cp "${RECIPE_WALLET_FIXTURE}" "$CANONICAL"
|
|
56
|
+
chmod 600 "$CANONICAL" 2>/dev/null || true
|
|
57
|
+
printf 'sync-wallet-fixture: linked wallet-fixture.json from RECIPE_WALLET_FIXTURE\n' >&2
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Farmslot slot sync (requires farmslot root and sync-fixtures.sh).
|
|
62
|
+
if [ -n "$SLOT_ID" ]; then
|
|
63
|
+
FARMSLOT_ROOT="${FARMSLOT_ROOT:-}"
|
|
64
|
+
for candidate in "$HOME/dev/farmslot" "$HOME/farmslot"; do
|
|
65
|
+
[ -d "$candidate/scripts" ] || continue
|
|
66
|
+
FARMSLOT_ROOT="$candidate"
|
|
67
|
+
break
|
|
68
|
+
done
|
|
69
|
+
if [ -n "$FARMSLOT_ROOT" ] && [ -f "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" ]; then
|
|
70
|
+
if bash "$FARMSLOT_ROOT/scripts/sync-fixtures.sh" --slot "$SLOT_ID" 2>/dev/null; then
|
|
71
|
+
[ -f "$CANONICAL" ] && exit 0
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# No fixture source found — informational non-zero (callers tolerate this).
|
|
77
|
+
printf 'sync-wallet-fixture: no wallet fixture source found for %s\n' "$TARGET" >&2
|
|
78
|
+
exit 1
|
package/bin/mm-harness
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# mm-harness — the single front door for the MetaMask recipe loop.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# manifest/live/runtime-*) stay reachable via their own bins until the caller sweep.
|
|
4
|
+
# The entry is the commander-based CLI (src/mm-harness-cli.ts) that presents the
|
|
5
|
+
# end-state surface; it resolves node/tsx/dist the same way whether the package is
|
|
6
|
+
# installed as a dependency or run from a source checkout.
|
|
8
7
|
set -euo pipefail
|
|
9
8
|
SOURCE="${BASH_SOURCE[0]}"
|
|
10
9
|
while [ -L "$SOURCE" ]; do
|
|
@@ -19,13 +18,13 @@ done
|
|
|
19
18
|
SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
|
|
20
19
|
RUNNER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
21
20
|
|
|
22
|
-
ENSURE_DEPS="$RUNNER_DIR/
|
|
21
|
+
ENSURE_DEPS="$RUNNER_DIR/adapters/shared/ensure-runner-deps.sh"
|
|
23
22
|
if [ -f "$ENSURE_DEPS" ]; then
|
|
24
23
|
# shellcheck disable=SC1090
|
|
25
24
|
bash "$ENSURE_DEPS" "$RUNNER_DIR"
|
|
26
25
|
fi
|
|
27
26
|
|
|
28
|
-
ENTRY_TS="$RUNNER_DIR/
|
|
27
|
+
ENTRY_TS="$RUNNER_DIR/src/mm-harness-cli.ts"
|
|
29
28
|
ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"
|
|
30
29
|
|
|
31
30
|
find_protocol_root() {
|
package/docs/CLI-SPEC.md
CHANGED
|
@@ -36,8 +36,8 @@ exhaustive —
|
|
|
36
36
|
- **REMOVE** — name/bin/subcommand deleted in the wave; **requires a one-line justification** (needs Arthur's sign-off).
|
|
37
37
|
|
|
38
38
|
**No row may be unmapped.** An unmapped row means the refactor is not allowed to
|
|
39
|
-
proceed. Source of truth: the bins' `case` statements + the naming audit
|
|
40
|
-
|
|
39
|
+
proceed. Source of truth: the bins' `case` statements + the naming audit.
|
|
40
|
+
Every row is real.
|
|
41
41
|
|
|
42
42
|
## Surface A — `metamask-recipe` typed CLI (`runner/src/cli.ts`)
|
|
43
43
|
|
|
@@ -913,3 +913,42 @@ The fleet is just another agent-grade caller:
|
|
|
913
913
|
slot's prepare hook calls the same verb with slot context; dispatch scales it
|
|
914
914
|
across the fleet.
|
|
915
915
|
|
|
916
|
+
## Appendix — Extension runtime lifecycle (internal notes)
|
|
917
|
+
|
|
918
|
+
Internal implementation notes for the Extension runtime lifecycle — the
|
|
919
|
+
self-healing path that gets a checkout from "installed" to "a live, verified
|
|
920
|
+
extension under CDP control". These are **not** a public command surface: the
|
|
921
|
+
probes below are internal functions, composed for you by `launch --verify` and by
|
|
922
|
+
the readiness commands (`doctor --fix`, `verify`). The runner is the source of
|
|
923
|
+
truth for Extension harness injection, readiness decisions, extension-id
|
|
924
|
+
resolution, and live CDP health checks.
|
|
925
|
+
|
|
926
|
+
Public entry points:
|
|
927
|
+
|
|
928
|
+
- **`install`** writes the Extension runtime overlay under the configured harness
|
|
929
|
+
root. CI/agents run it explicitly; the daily-loop commands auto-ensure it.
|
|
930
|
+
- **`verify`** confirms the overlay is present and healthy — a live CDP health
|
|
931
|
+
probe when a runtime is reachable, no launch.
|
|
932
|
+
- **`launch --verify`** installs the overlay if missing, launches/attaches the
|
|
933
|
+
extension, and polls CDP until the runtime is ready.
|
|
934
|
+
- **`doctor` / `doctor --fix`** report readiness and (with `--fix`) run the
|
|
935
|
+
healing steps below WITHOUT starting the app.
|
|
936
|
+
|
|
937
|
+
Internal lifecycle stages, composed in order (each idempotent, each reporting the
|
|
938
|
+
cheapest next action so the path converges rather than rebuilding from scratch):
|
|
939
|
+
|
|
940
|
+
- **Extension-id resolution** — resolves the deterministic unpacked extension id
|
|
941
|
+
from `dist/chrome/manifest.json`, optionally verified against a live CDP target.
|
|
942
|
+
Runs first; every later stage keys off this id.
|
|
943
|
+
- **Readiness decision** — inspects the checkout + live browser and returns the
|
|
944
|
+
cheapest next action (`install`, `build`, `relaunch`, or `ready`) plus the
|
|
945
|
+
concrete `actions[]` to execute.
|
|
946
|
+
- **Runtime launch** — launches Chrome with the installed harness helper, seeds
|
|
947
|
+
the wallet fixture, and runs live smoke verification. Two tiers: reuse the
|
|
948
|
+
existing dist (quick relaunch), or the clean-build path (clear webpack cache,
|
|
949
|
+
start the harness-owned watcher, wait for a clean compile, then launch + verify).
|
|
950
|
+
- **Convergence** — converges the live browser to exactly one healthy `home.html`
|
|
951
|
+
tab (closing strays, reopening if needed) and confirms it with the health probe.
|
|
952
|
+
- **Health probe** — read-only liveness probe for the running extension over CDP;
|
|
953
|
+
the final gate of convergence and the standalone answer for `verify`.
|
|
954
|
+
|