@deeeed/metamask-harness 0.5.0 → 0.6.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 +51 -20
- package/adapters/core/inject.sh +1 -0
- package/adapters/extension/cleanup.mjs +1 -1
- package/adapters/extension/inject.mjs +4 -1
- package/adapters/extension/launch-browser.cjs +18 -4
- package/adapters/extension/live.sh +14 -2
- package/adapters/extension/refresh-build.sh +2 -2
- package/adapters/extension/sidepanel-toggle.sh +33 -12
- package/adapters/extension/start-watch.sh +2 -2
- package/adapters/extension/wallet-fixture-state.cjs +38 -24
- package/adapters/manifest.json +32 -3
- package/adapters/mobile/inject.sh +1 -0
- package/adapters/mobile/start-metro.sh +1 -1
- package/adapters/mobile/stop-metro.sh +20 -0
- package/adapters/mobile/verify.sh +1 -1
- package/adapters/shared/activate-repo-node.sh +1 -1
- package/adapters/shared/cli-ux.sh +44 -25
- package/adapters/shared/ensure-runner-deps.sh +30 -0
- package/adapters/shared/log-tui.mjs +4 -4
- package/adapters/shared/open-debug.mjs +1 -1
- package/adapters/shared/reap-checkout-metros.sh +53 -0
- package/adapters/shared/recipe-harness-root.mjs +23 -0
- package/adapters/shared/resolve-farmslot-ports-core.mjs +205 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +20 -0
- package/adapters/shared/resolve-farmslot-ports.sh +19 -126
- package/bin/mm-harness +45 -4
- package/dist/adapters/extension/runtime.js +3 -1
- package/dist/adapters/mobile/provision.js +34 -1
- package/dist/adapters/mobile/runtime-decision.js +3 -1
- package/dist/adapters/resolve-farmslot-ports.js +22 -0
- package/dist/adapters/slot-ports.js +18 -29
- package/dist/cli.js +32 -1463
- package/dist/commands/call.js +183 -0
- package/dist/commands/completion-candidates.js +58 -0
- package/dist/commands/doctor.js +101 -0
- package/dist/commands/ensure-ready.js +24 -0
- package/dist/commands/flows.js +62 -0
- package/dist/commands/launch/extension.js +40 -0
- package/dist/commands/{launch.js → launch/index.js} +15 -47
- package/dist/commands/launch/mobile.js +10 -0
- package/dist/commands/manifest.js +72 -0
- package/dist/commands/parse-args.js +189 -0
- package/dist/commands/provision.js +136 -0
- package/dist/commands/resolve-extension.js +23 -0
- package/dist/commands/run-engine.js +341 -0
- package/dist/commands/run.js +217 -0
- package/dist/commands/runtime-decision.js +58 -0
- package/dist/commands/runtime-health.js +25 -0
- package/dist/commands/runtime-launch.js +139 -0
- package/dist/commands/self-test.js +52 -0
- package/dist/commands/stop.js +52 -0
- package/dist/harness.js +8 -48
- package/dist/mm-harness-cli.js +13 -8
- package/docs/CLI-SPEC.md +1 -1
- package/docs/CODE-MAP.md +62 -0
- package/library/README.md +14 -0
- 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 +7 -1
- package/library/actions/harness-exports.mjs +27 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +14 -2
- package/library/actions/mobile/wallet/setup.mjs +1 -1
- package/package.json +6 -7
- package/src/adapters/core/surface.ts +0 -71
- package/src/adapters/extension/ensure-ready.ts +0 -185
- package/src/adapters/extension/extension-id.ts +0 -107
- package/src/adapters/extension/runtime-decision.ts +0 -445
- package/src/adapters/extension/runtime.ts +0 -407
- package/src/adapters/extension/surface.ts +0 -88
- package/src/adapters/mobile/deps-markers.ts +0 -21
- package/src/adapters/mobile/prepare.ts +0 -246
- package/src/adapters/mobile/provision.ts +0 -594
- package/src/adapters/mobile/runtime-decision.ts +0 -459
- package/src/adapters/mobile/surface.ts +0 -71
- package/src/adapters/slot-ports.ts +0 -165
- package/src/adapters/surface.ts +0 -117
- package/src/adapters.ts +0 -601
- package/src/cli-color.ts +0 -92
- package/src/cli-commands.ts +0 -250
- package/src/cli-version.ts +0 -141
- package/src/cli.ts +0 -2091
- package/src/commands/debug.ts +0 -65
- package/src/commands/fixtures.ts +0 -198
- package/src/commands/launch.ts +0 -470
- package/src/commands/logs.ts +0 -99
- package/src/commands/shared.ts +0 -235
- package/src/commands/update.ts +0 -316
- package/src/completions-cache.ts +0 -86
- package/src/doctor.ts +0 -215
- package/src/harness.ts +0 -797
- package/src/heal-bounds.ts +0 -198
- package/src/index.ts +0 -15
- package/src/leaf-invoke.ts +0 -28
- package/src/live-adapter-contract.ts +0 -274
- package/src/manifest.ts +0 -47
- package/src/mm-harness-cli.ts +0 -655
- package/src/paths.ts +0 -198
- package/src/progress.ts +0 -117
- package/src/recording-target.ts +0 -147
- package/src/run-recording.ts +0 -329
- package/src/runner.ts +0 -108
- package/src/types.ts +0 -57
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# resolve-farmslot-ports.sh — bash-compat wrapper over resolve-farmslot-ports.mjs
|
|
2
3
|
#
|
|
3
4
|
# Farmslot pool JSON is authoritative when a slot repo matches; otherwise fall back
|
|
4
5
|
# to the local-extension-N formula (6660+N / 9010+N).
|
|
@@ -8,85 +9,15 @@
|
|
|
8
9
|
# resolve_extension_runtime_ports /path/to/metamask-extension-N
|
|
9
10
|
# -> prints CDP_PORT=... WATCHER_PORT=... SLOT_ID=... lines
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
local farmslot_root="${FARMSLOT_ROOT:-}"
|
|
14
|
-
[ -n "$repo" ] || return 1
|
|
15
|
-
repo="$(cd "$repo" && pwd -P)"
|
|
16
|
-
|
|
17
|
-
local root candidate match
|
|
18
|
-
for root in "$farmslot_root" "$HOME/dev/farmslot"; do
|
|
19
|
-
[ -n "$root" ] && [ -d "$root/pool" ] || continue
|
|
20
|
-
match="$(POOL_ROOT="$root" REPO_TARGET="$repo" python3 - <<'PY'
|
|
21
|
-
import glob, json, os, re, sys
|
|
22
|
-
|
|
23
|
-
pool_root = os.environ["POOL_ROOT"]
|
|
24
|
-
repo = os.path.realpath(os.environ["REPO_TARGET"])
|
|
25
|
-
base = os.path.basename(repo)
|
|
26
|
-
slot_suffix = None
|
|
27
|
-
m = re.search(r"-(\d+)$", base)
|
|
28
|
-
if m:
|
|
29
|
-
slot_suffix = m.group(1)
|
|
30
|
-
|
|
31
|
-
def score_slot(slot):
|
|
32
|
-
resources = slot.get("resources") or {}
|
|
33
|
-
cdp = (resources.get("browser") or {}).get("cdp_port")
|
|
34
|
-
port = (resources.get("dev-server") or {}).get("port")
|
|
35
|
-
session = slot.get("session") or ""
|
|
36
|
-
slot_id = slot.get("id") or ""
|
|
37
|
-
score = 0
|
|
38
|
-
if cdp is not None:
|
|
39
|
-
score += 100
|
|
40
|
-
if port is not None:
|
|
41
|
-
score += 10
|
|
42
|
-
if slot_suffix:
|
|
43
|
-
if session == f"mme-{slot_suffix}":
|
|
44
|
-
score += 50
|
|
45
|
-
if slot_id.endswith(f"mme-{slot_suffix}") or f"-mme-{slot_suffix}" in slot_id:
|
|
46
|
-
score += 40
|
|
47
|
-
if "demo" not in slot_id.lower():
|
|
48
|
-
score += 5
|
|
49
|
-
return score, cdp, port, slot_id
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
_RESOLVE_PORTS_MJS="$SCRIPT_DIR/resolve-farmslot-ports.mjs"
|
|
50
14
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
continue
|
|
55
|
-
try:
|
|
56
|
-
data = json.load(open(path, encoding="utf-8"))
|
|
57
|
-
except Exception:
|
|
58
|
-
continue
|
|
59
|
-
for slot in data.get("slots") or []:
|
|
60
|
-
slot_repo = slot.get("repo") or ""
|
|
61
|
-
if not slot_repo:
|
|
62
|
-
continue
|
|
63
|
-
try:
|
|
64
|
-
if os.path.realpath(slot_repo) != repo:
|
|
65
|
-
continue
|
|
66
|
-
except Exception:
|
|
67
|
-
continue
|
|
68
|
-
scored = score_slot(slot)
|
|
69
|
-
if best is None or scored[0] > best[0]:
|
|
70
|
-
best = scored
|
|
71
|
-
|
|
72
|
-
if best is None:
|
|
73
|
-
sys.exit(1)
|
|
15
|
+
_resolve_ports_cli() {
|
|
16
|
+
node "$_RESOLVE_PORTS_MJS" "$1" "${2:-.}"
|
|
17
|
+
}
|
|
74
18
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
print(f"CDP_PORT={cdp}")
|
|
78
|
-
if port is not None:
|
|
79
|
-
print(f"WATCHER_PORT={port}")
|
|
80
|
-
if slot_id:
|
|
81
|
-
print(f"SLOT_ID={slot_id}")
|
|
82
|
-
sys.exit(0)
|
|
83
|
-
PY
|
|
84
|
-
)" || continue
|
|
85
|
-
[ -n "$match" ] || continue
|
|
86
|
-
printf '%s\n' "$match"
|
|
87
|
-
return 0
|
|
88
|
-
done
|
|
89
|
-
return 1
|
|
19
|
+
resolve_farmslot_ports_by_repo() {
|
|
20
|
+
_resolve_ports_cli resolve_farmslot_ports_by_repo "$1"
|
|
90
21
|
}
|
|
91
22
|
|
|
92
23
|
infer_extension_slot_suffix() {
|
|
@@ -100,27 +31,18 @@ infer_extension_slot_suffix() {
|
|
|
100
31
|
}
|
|
101
32
|
|
|
102
33
|
resolve_default_extension_ports() {
|
|
103
|
-
|
|
104
|
-
n="$(infer_extension_slot_suffix "$repo" || true)"
|
|
105
|
-
[ -n "$n" ] || return 1
|
|
106
|
-
printf 'CDP_PORT=%s\n' "$((6660 + n))"
|
|
107
|
-
printf 'WATCHER_PORT=%s\n' "$((9010 + n))"
|
|
108
|
-
printf 'SLOT_ID=local-extension-%s\n' "$n"
|
|
34
|
+
_resolve_ports_cli resolve_default_extension_ports "$1"
|
|
109
35
|
}
|
|
110
36
|
|
|
111
37
|
resolve_extension_runtime_ports() {
|
|
112
|
-
|
|
113
|
-
if resolve_farmslot_ports_by_repo "$repo"; then
|
|
114
|
-
return 0
|
|
115
|
-
fi
|
|
116
|
-
resolve_default_extension_ports "$repo"
|
|
38
|
+
_resolve_ports_cli resolve_extension_runtime_ports "${1:-.}"
|
|
117
39
|
}
|
|
118
40
|
|
|
119
41
|
apply_resolved_extension_ports() {
|
|
120
42
|
local repo="${1:-.}" line key val from_pool=false resolved=""
|
|
121
|
-
if resolved="$(resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
|
|
43
|
+
if resolved="$(_resolve_ports_cli resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
|
|
122
44
|
from_pool=true
|
|
123
|
-
elif resolved="$(resolve_default_extension_ports "$repo" 2>/dev/null)"; then
|
|
45
|
+
elif resolved="$(_resolve_ports_cli resolve_default_extension_ports "$repo" 2>/dev/null)"; then
|
|
124
46
|
from_pool=false
|
|
125
47
|
else
|
|
126
48
|
return 0
|
|
@@ -143,52 +65,23 @@ apply_resolved_extension_ports() {
|
|
|
143
65
|
done <<< "$resolved"
|
|
144
66
|
}
|
|
145
67
|
|
|
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
68
|
resolve_mobile_slot_defaults() {
|
|
150
|
-
|
|
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"
|
|
69
|
+
_resolve_ports_cli resolve_mobile_slot_defaults "$1"
|
|
156
70
|
}
|
|
157
71
|
|
|
158
|
-
# Slot context the orchestrator's prepare wrote INTO the checkout — the
|
|
159
|
-
# authoritative device/port mapping for this repo (survives pool renames that
|
|
160
|
-
# break any naming formula). Highest-priority detection source.
|
|
161
72
|
resolve_mobile_runtime_context() {
|
|
162
|
-
|
|
163
|
-
ctx="$repo/${RECIPE_RUNTIME_DIR:-temp/recipe/runtime}/agentic-runtime.json"
|
|
164
|
-
[ -f "$ctx" ] || return 1
|
|
165
|
-
node -e '
|
|
166
|
-
try {
|
|
167
|
-
const c = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
|
|
168
|
-
if (!c.simulator && !c.metroPort) process.exit(1);
|
|
169
|
-
if (c.metroPort) console.log(`WATCHER_PORT=${c.metroPort}`);
|
|
170
|
-
if (c.simulator) console.log(`IOS_SIMULATOR=${c.simulator}`);
|
|
171
|
-
if (c.slotId) console.log(`SLOT_ID=${c.slotId}`);
|
|
172
|
-
} catch { process.exit(1); }
|
|
173
|
-
' "$ctx" 2>/dev/null
|
|
73
|
+
_resolve_ports_cli resolve_mobile_runtime_context "$1"
|
|
174
74
|
}
|
|
175
75
|
|
|
176
76
|
resolve_mobile_runtime_ports() {
|
|
177
|
-
|
|
178
|
-
if resolve_mobile_runtime_context "$repo"; then
|
|
179
|
-
return 0
|
|
180
|
-
fi
|
|
181
|
-
if resolve_farmslot_ports_by_repo "$repo"; then
|
|
182
|
-
return 0
|
|
183
|
-
fi
|
|
184
|
-
resolve_mobile_slot_defaults "$repo"
|
|
77
|
+
_resolve_ports_cli resolve_mobile_runtime_ports "${1:-.}"
|
|
185
78
|
}
|
|
186
79
|
|
|
187
80
|
apply_resolved_mobile_ports() {
|
|
188
81
|
local repo="${1:-.}" line key val from_pool=false resolved=""
|
|
189
|
-
if resolved="$(resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
|
|
82
|
+
if resolved="$(_resolve_ports_cli resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
|
|
190
83
|
from_pool=true
|
|
191
|
-
elif resolved="$(resolve_mobile_slot_defaults "$repo" 2>/dev/null)"; then
|
|
84
|
+
elif resolved="$(_resolve_ports_cli resolve_mobile_slot_defaults "$repo" 2>/dev/null)"; then
|
|
192
85
|
from_pool=false
|
|
193
86
|
else
|
|
194
87
|
return 0
|
|
@@ -209,4 +102,4 @@ apply_resolved_mobile_ports() {
|
|
|
209
102
|
;;
|
|
210
103
|
esac
|
|
211
104
|
done <<< "$resolved"
|
|
212
|
-
}
|
|
105
|
+
}
|
package/bin/mm-harness
CHANGED
|
@@ -50,15 +50,27 @@ if [ -n "${MM_HARNESS_BIN:-}" ]; then
|
|
|
50
50
|
fi
|
|
51
51
|
fi
|
|
52
52
|
|
|
53
|
+
ENTRY_TS="$RUNNER_DIR/src/mm-harness-cli.ts"
|
|
54
|
+
ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"
|
|
55
|
+
|
|
56
|
+
# Published installs ship dist/ only. Teach before deps work so a broken global
|
|
57
|
+
# install never runs ensure-runner-deps against a partial node_modules tree.
|
|
58
|
+
case "$RUNNER_DIR/" in
|
|
59
|
+
*/node_modules/*)
|
|
60
|
+
if [ ! -f "$ENTRY_DIST" ]; then
|
|
61
|
+
echo "mm-harness: installed package is missing dist/mm-harness-cli.js." >&2
|
|
62
|
+
echo " Next: reinstall @deeeed/metamask-harness@latest (npm i -g @deeeed/metamask-harness@latest)" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
|
|
53
68
|
ENSURE_DEPS="$RUNNER_DIR/adapters/shared/ensure-runner-deps.sh"
|
|
54
69
|
if [ -f "$ENSURE_DEPS" ]; then
|
|
55
70
|
# shellcheck disable=SC1090
|
|
56
71
|
bash "$ENSURE_DEPS" "$RUNNER_DIR"
|
|
57
72
|
fi
|
|
58
73
|
|
|
59
|
-
ENTRY_TS="$RUNNER_DIR/src/mm-harness-cli.ts"
|
|
60
|
-
ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"
|
|
61
|
-
|
|
62
74
|
find_protocol_root() {
|
|
63
75
|
local start="$1"
|
|
64
76
|
local dir
|
|
@@ -97,10 +109,38 @@ node_can_run_source_typescript() {
|
|
|
97
109
|
rm -rf "$probe_dir"
|
|
98
110
|
}
|
|
99
111
|
|
|
112
|
+
# In a source checkout dist wins when present — make that visible, and warn when
|
|
113
|
+
# the compiled entry is older than the sources it shadows (a stale dist silently
|
|
114
|
+
# runs old code while you edit src/).
|
|
115
|
+
if [ -f "$ENTRY_DIST" ] && [ -f "$ENTRY_TS" ]; then
|
|
116
|
+
export MM_HARNESS_RUN_MODE="dist"
|
|
117
|
+
if find "$RUNNER_DIR/src" -name '*.ts' -newer "$ENTRY_DIST" 2>/dev/null | head -1 | grep -q .; then
|
|
118
|
+
echo "mm-harness: dist/ is OLDER than src/ — this run uses the stale compiled code." >&2
|
|
119
|
+
echo " Next: npm run build (or: trash dist/ to run straight from src via tsx)" >&2
|
|
120
|
+
fi
|
|
121
|
+
elif [ -f "$ENTRY_TS" ]; then
|
|
122
|
+
export MM_HARNESS_RUN_MODE="src"
|
|
123
|
+
fi
|
|
124
|
+
|
|
100
125
|
if [ -f "$ENTRY_DIST" ]; then
|
|
101
126
|
exec node "$ENTRY_DIST" "$@"
|
|
102
127
|
fi
|
|
103
128
|
|
|
129
|
+
if [ -f "$ENTRY_TS" ]; then
|
|
130
|
+
case " ${NODE_OPTIONS:-} " in
|
|
131
|
+
*" --conditions=development "*) ;;
|
|
132
|
+
*)
|
|
133
|
+
export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--conditions=development"
|
|
134
|
+
;;
|
|
135
|
+
esac
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
if [ ! -f "$ENTRY_TS" ]; then
|
|
139
|
+
echo "mm-harness: no CLI entry found (expected dist/mm-harness-cli.js or src/mm-harness-cli.ts)." >&2
|
|
140
|
+
echo " Next: from a source checkout run npm run build, or install dependencies (tsx) for the dev path" >&2
|
|
141
|
+
exit 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
104
144
|
TSX_BIN="${TSX_BIN:-$RUNNER_DIR/node_modules/.bin/tsx}"
|
|
105
145
|
if [ ! -x "$TSX_BIN" ] && [ -x "$RUNNER_DIR/../../.bin/tsx" ]; then
|
|
106
146
|
TSX_BIN="$RUNNER_DIR/../../.bin/tsx"
|
|
@@ -126,5 +166,6 @@ if [ -x "$TSX_BIN" ]; then
|
|
|
126
166
|
exec "$TSX_BIN" "$ENTRY_TS" "$@"
|
|
127
167
|
fi
|
|
128
168
|
|
|
129
|
-
echo "mm-harness source checkout requires dist/mm-harness-cli.js, Node.js TypeScript type stripping, or a local tsx binary.
|
|
169
|
+
echo "mm-harness: source checkout requires dist/mm-harness-cli.js, Node.js TypeScript type stripping, or a local tsx binary." >&2
|
|
170
|
+
echo " Next: npm run build or yarn install (tsx devDependency) or set TSX_BIN for local protocol co-development" >&2
|
|
130
171
|
exit 1
|
|
@@ -43,6 +43,7 @@ async function assertHealthyExtensionRuntime(options) {
|
|
|
43
43
|
}
|
|
44
44
|
async function checkExtensionRuntimeHealth(projectRoot, cdpPort) {
|
|
45
45
|
const findings = [];
|
|
46
|
+
const warnings = [];
|
|
46
47
|
let targets = [];
|
|
47
48
|
try {
|
|
48
49
|
await jsonGet(`http://127.0.0.1:${cdpPort}/json/version`);
|
|
@@ -87,11 +88,12 @@ async function checkExtensionRuntimeHealth(projectRoot, cdpPort) {
|
|
|
87
88
|
findings.push("stateHooks.getPerpsStreamManager is unavailable.");
|
|
88
89
|
}
|
|
89
90
|
if (runtime.backgroundProbeOk !== true) {
|
|
90
|
-
|
|
91
|
+
warnings.push(`Perps background read probe failed: ${runtime.backgroundProbeError ?? "unknown error"}.`);
|
|
91
92
|
}
|
|
92
93
|
const extensionId = safeExtensionId(target);
|
|
93
94
|
return {
|
|
94
95
|
status: findings.length === 0 ? "PASS" : "FAIL",
|
|
96
|
+
warnings,
|
|
95
97
|
cdpPort,
|
|
96
98
|
targetUrl: target.url,
|
|
97
99
|
extensionId,
|
|
@@ -4,6 +4,7 @@ import fs from "node:fs";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { recipeRuntimeDir } from "../../paths.js";
|
|
7
|
+
import { resolveFarmslotPortsByRepo } from "../../../adapters/shared/resolve-farmslot-ports-core.mjs";
|
|
7
8
|
const RUNWAY_IOS_METADATA = {
|
|
8
9
|
artifactName: "ios-app-main-dev-expo",
|
|
9
10
|
workflow: "expo-dev-build.yml",
|
|
@@ -11,6 +12,19 @@ const RUNWAY_IOS_METADATA = {
|
|
|
11
12
|
appDirName: "MetaMask.app",
|
|
12
13
|
fallbackRepo: "MetaMask/metamask-mobile"
|
|
13
14
|
};
|
|
15
|
+
function resolvePoolIdentity(target) {
|
|
16
|
+
const out = resolveFarmslotPortsByRepo(target);
|
|
17
|
+
const id = {};
|
|
18
|
+
if (!out) return id;
|
|
19
|
+
for (const line of String(out).split("\n")) {
|
|
20
|
+
const m = /^([A-Z_]+)=(.+)$/u.exec(line.trim());
|
|
21
|
+
if (!m || m[2] === "null" || m[2] === "") continue;
|
|
22
|
+
if (m[1] === "IOS_SIMULATOR") id.simulator = m[2];
|
|
23
|
+
else if (m[1] === "WATCHER_PORT") id.watcherPort = m[2];
|
|
24
|
+
else if (m[1] === "SLOT_ID") id.slotId = m[2];
|
|
25
|
+
}
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
14
28
|
async function provisionRunwayMobile(target, options) {
|
|
15
29
|
const resolvedTarget = path.resolve(target);
|
|
16
30
|
const platform = options.platform ?? "ios";
|
|
@@ -21,7 +35,14 @@ async function provisionRunwayMobile(target, options) {
|
|
|
21
35
|
if (platform !== "ios") {
|
|
22
36
|
return fail(resolvedTarget, platform, "UNSUPPORTED_PLATFORM", "runway provisioning currently installs the iOS .app artifact only.", command, slot);
|
|
23
37
|
}
|
|
24
|
-
|
|
38
|
+
let simulator = options.simulator ?? slot.simulator ?? process.env.IOS_SIMULATOR;
|
|
39
|
+
try {
|
|
40
|
+
const pool = resolvePoolIdentity(resolvedTarget);
|
|
41
|
+
if (!simulator && pool.simulator) simulator = pool.simulator;
|
|
42
|
+
if (!slot.slotId && pool.slotId) slot.slotId = pool.slotId;
|
|
43
|
+
if (!slot.watcherPort && pool.watcherPort) slot.watcherPort = pool.watcherPort;
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
25
46
|
if (!simulator && !options.resolveOnly) {
|
|
26
47
|
return fail(resolvedTarget, platform, "SIMULATOR_MISSING", "no simulator was resolved from agentic-runtime.json, --simulator, or IOS_SIMULATOR.", command, slot);
|
|
27
48
|
}
|
|
@@ -72,6 +93,7 @@ async function provisionRunwayMobile(target, options) {
|
|
|
72
93
|
const cacheRoot = options.cacheRoot ?? defaultRunwayCacheRoot();
|
|
73
94
|
const cache = ensureCachedArtifact(repo, resolved.branch, resolved.runId, cacheRoot, options);
|
|
74
95
|
log(options, `runway: installing ${cache.artifact.appPath} on ${sim.name}`);
|
|
96
|
+
bootSimulator(sim.udid ?? sim.name, options);
|
|
75
97
|
execFileSync("xcrun", ["simctl", "install", sim.udid ?? sim.name, cache.artifact.appPath], { stdio: ["ignore", "ignore", "pipe"] });
|
|
76
98
|
const baselinePath = writeRunwayBaseline(resolvedTarget, slot, platform, resolved, cache.artifact, sim, false, options.runtimeDir);
|
|
77
99
|
return {
|
|
@@ -379,6 +401,17 @@ function hashPath(root) {
|
|
|
379
401
|
visitDirectory(root);
|
|
380
402
|
return { sizeBytes, sha256: hash.digest("hex") };
|
|
381
403
|
}
|
|
404
|
+
function bootSimulator(device, options) {
|
|
405
|
+
try {
|
|
406
|
+
execFileSync("xcrun", ["simctl", "boot", device], { stdio: ["ignore", "ignore", "ignore"] });
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
execFileSync("xcrun", ["simctl", "bootstatus", device, "-b"], { stdio: ["ignore", "ignore", "ignore"], timeout: 12e4 });
|
|
411
|
+
} catch {
|
|
412
|
+
log(options, `runway: bootstatus wait for ${device} did not confirm; continuing to install`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
382
415
|
function ensureSimulator(name, runtime, deviceType) {
|
|
383
416
|
const existing = findSimulator(name);
|
|
384
417
|
if (existing) return { name, udid: existing, created: false, runtime, deviceType };
|
|
@@ -103,6 +103,8 @@ async function decideMobileReadiness(target, options = {}) {
|
|
|
103
103
|
const fast = options.preflightMode === "fast";
|
|
104
104
|
const report = await computeMobileReadiness(resolved, options, fast);
|
|
105
105
|
if (fast && report.decision === "install") {
|
|
106
|
+
const hasNodeModules = fs.existsSync(path.join(resolved, "node_modules"));
|
|
107
|
+
const standalone = hasNodeModules ? "`yarn setup:expo --no-build-ios --no-build-android`" : "`yarn install --immutable && yarn setup:expo --no-build-ios --no-build-android`";
|
|
106
108
|
return {
|
|
107
109
|
...report,
|
|
108
110
|
decision: "blocked",
|
|
@@ -111,7 +113,7 @@ async function decideMobileReadiness(target, options = {}) {
|
|
|
111
113
|
"Fast preflight found dependencies not ready and does not install them (the orchestrator deps phase owns installation).",
|
|
112
114
|
...report.reasons
|
|
113
115
|
],
|
|
114
|
-
userAction:
|
|
116
|
+
userAction: `run the slot deps/prepare phase; standalone: ${standalone} in the checkout, then re-run \`mm-harness launch <platform>\``,
|
|
115
117
|
actions: []
|
|
116
118
|
};
|
|
117
119
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatKvLines,
|
|
3
|
+
inferSlotSuffix,
|
|
4
|
+
realRepoPath,
|
|
5
|
+
resolveDefaultExtensionPorts,
|
|
6
|
+
resolveExtensionRuntimePorts,
|
|
7
|
+
resolveFarmslotPortsByRepo,
|
|
8
|
+
resolveMobileRuntimeContext,
|
|
9
|
+
resolveMobileRuntimePorts,
|
|
10
|
+
resolveMobileSlotDefaults
|
|
11
|
+
} from "../../adapters/shared/resolve-farmslot-ports-core.mjs";
|
|
12
|
+
export {
|
|
13
|
+
formatKvLines,
|
|
14
|
+
inferSlotSuffix,
|
|
15
|
+
realRepoPath,
|
|
16
|
+
resolveDefaultExtensionPorts,
|
|
17
|
+
resolveExtensionRuntimePorts,
|
|
18
|
+
resolveFarmslotPortsByRepo,
|
|
19
|
+
resolveMobileRuntimeContext,
|
|
20
|
+
resolveMobileRuntimePorts,
|
|
21
|
+
resolveMobileSlotDefaults
|
|
22
|
+
};
|
|
@@ -2,12 +2,19 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { readRuntimeContextField, resolveRuntimeContextPath } from "../harness.js";
|
|
5
|
-
import { recipeRuntimeDir
|
|
5
|
+
import { recipeRuntimeDir } from "../paths.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveDefaultExtensionPorts,
|
|
8
|
+
resolveFarmslotPortsByRepo,
|
|
9
|
+
resolveMobileRuntimeContext,
|
|
10
|
+
resolveMobileSlotDefaults
|
|
11
|
+
} from "./resolve-farmslot-ports.js";
|
|
6
12
|
function applyKVLines(output, overwrite) {
|
|
7
13
|
for (const line of output.split("\n")) {
|
|
8
14
|
const m = /^([A-Z_]+)=(.+)$/u.exec(line.trim());
|
|
9
15
|
if (!m) continue;
|
|
10
16
|
const [, key, val] = m;
|
|
17
|
+
if (val === "null" || val === "undefined" || val === "") continue;
|
|
11
18
|
switch (key) {
|
|
12
19
|
case "WATCHER_PORT":
|
|
13
20
|
if (overwrite || !process.env["WATCHER_PORT"]) {
|
|
@@ -31,31 +38,13 @@ function applyKVLines(output, overwrite) {
|
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
|
-
function sourceResolve(fn, target) {
|
|
35
|
-
const resolveScript = path.join(runnerDir, "adapters/shared/resolve-farmslot-ports.sh");
|
|
36
|
-
try {
|
|
37
|
-
return execFileSync("bash", ["-c", `source "${resolveScript}" && ${fn} "${target}"`], {
|
|
38
|
-
encoding: "utf8",
|
|
39
|
-
timeout: 5e3,
|
|
40
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
41
|
-
});
|
|
42
|
-
} catch {
|
|
43
|
-
return "";
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
41
|
function resolveMobileSlotPorts(target) {
|
|
47
|
-
const ctxOut =
|
|
48
|
-
if (ctxOut
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (poolOut.trim()) {
|
|
54
|
-
applyKVLines(poolOut, true);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const defOut = sourceResolve("resolve_mobile_slot_defaults", target);
|
|
58
|
-
if (defOut.trim()) applyKVLines(defOut, false);
|
|
42
|
+
const ctxOut = resolveMobileRuntimeContext(target);
|
|
43
|
+
if (ctxOut?.trim()) applyKVLines(ctxOut, true);
|
|
44
|
+
const poolOut = resolveFarmslotPortsByRepo(target);
|
|
45
|
+
if (poolOut?.trim()) applyKVLines(poolOut, false);
|
|
46
|
+
const defOut = resolveMobileSlotDefaults(target);
|
|
47
|
+
if (defOut?.trim()) applyKVLines(defOut, false);
|
|
59
48
|
}
|
|
60
49
|
function resolveExtensionSlotPorts(target) {
|
|
61
50
|
const contextPath = resolveRuntimeContextPath(target);
|
|
@@ -70,13 +59,13 @@ function resolveExtensionSlotPorts(target) {
|
|
|
70
59
|
process.env["RECIPE_WATCHER_PORT"] = dev;
|
|
71
60
|
}
|
|
72
61
|
if (process.env["CDP_PORT"]) return;
|
|
73
|
-
const poolOut =
|
|
74
|
-
if (poolOut
|
|
62
|
+
const poolOut = resolveFarmslotPortsByRepo(target);
|
|
63
|
+
if (poolOut?.trim()) {
|
|
75
64
|
applyKVLines(poolOut, true);
|
|
76
65
|
return;
|
|
77
66
|
}
|
|
78
|
-
const defOut =
|
|
79
|
-
if (defOut
|
|
67
|
+
const defOut = resolveDefaultExtensionPorts(target);
|
|
68
|
+
if (defOut?.trim()) applyKVLines(defOut, false);
|
|
80
69
|
}
|
|
81
70
|
function stopExtensionWatcher(target) {
|
|
82
71
|
const runtimeAbs = path.join(target, recipeRuntimeDir());
|