@deeeed/metamask-harness 0.5.1 → 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 +37 -20
- package/adapters/core/inject.sh +1 -0
- package/adapters/extension/cleanup.mjs +1 -1
- package/adapters/extension/inject.mjs +2 -1
- package/adapters/extension/launch-browser.cjs +18 -4
- package/adapters/extension/live.sh +14 -2
- package/adapters/extension/sidepanel-toggle.sh +33 -12
- package/adapters/extension/wallet-fixture-state.cjs +38 -24
- package/adapters/manifest.json +13 -0
- package/adapters/mobile/inject.sh +1 -0
- package/adapters/mobile/stop-metro.sh +20 -0
- package/adapters/shared/cli-ux.sh +20 -2
- package/adapters/shared/ensure-runner-deps.sh +30 -0
- 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 +15 -6
- 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/slot-ports.js +3 -8
- 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/extension/platform/cdp.mjs +1 -1
- package/library/actions/extension/wallet/ensure_unlocked.mjs +6 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +13 -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 -466
- package/src/adapters/mobile/surface.ts +0 -71
- package/src/adapters/resolve-farmslot-ports.ts +0 -13
- package/src/adapters/slot-ports.ts +0 -158
- 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
|
@@ -51,6 +51,7 @@ function scoreSlot(slot, slotSuffix) {
|
|
|
51
51
|
const resources = slot.resources ?? {};
|
|
52
52
|
const cdp = resources.browser?.cdp_port;
|
|
53
53
|
const port = resources['dev-server']?.port;
|
|
54
|
+
const simulator = resources['ios-sim']?.simulator;
|
|
54
55
|
const session = slot.session ?? '';
|
|
55
56
|
const slotId = slot.id ?? '';
|
|
56
57
|
let score = 0;
|
|
@@ -61,7 +62,7 @@ function scoreSlot(slot, slotSuffix) {
|
|
|
61
62
|
if (slotId.endsWith(`mme-${slotSuffix}`) || slotId.includes(`-mme-${slotSuffix}`)) score += 40;
|
|
62
63
|
}
|
|
63
64
|
if (!slotId.toLowerCase().includes('demo')) score += 5;
|
|
64
|
-
return [score, cdp, port, slotId];
|
|
65
|
+
return [score, cdp, port, slotId, simulator];
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export function resolveFarmslotPortsByRepo(repo) {
|
|
@@ -70,6 +71,12 @@ export function resolveFarmslotPortsByRepo(repo) {
|
|
|
70
71
|
|
|
71
72
|
const slotSuffix = inferSlotSuffix(realRepo);
|
|
72
73
|
const roots = [];
|
|
74
|
+
// A slot checkout lives at <workspace>/repos/<slot>; that workspace's own
|
|
75
|
+
// pool is the authoritative one for this repo — machine-global fallbacks
|
|
76
|
+
// (env root, dev checkout) apply only when the repo is not workspace-shaped.
|
|
77
|
+
if (path.basename(path.dirname(realRepo)) === 'repos') {
|
|
78
|
+
roots.push(path.join(path.dirname(path.dirname(realRepo)), 'farmslot'));
|
|
79
|
+
}
|
|
73
80
|
if (process.env.FARMSLOT_ROOT) roots.push(process.env.FARMSLOT_ROOT);
|
|
74
81
|
roots.push(path.join(os.homedir(), 'dev', 'farmslot'));
|
|
75
82
|
|
|
@@ -102,11 +109,12 @@ export function resolveFarmslotPortsByRepo(repo) {
|
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
if (best) {
|
|
105
|
-
const [, cdp, port, slotId] = best;
|
|
112
|
+
const [, cdp, port, slotId, simulator] = best;
|
|
106
113
|
return formatKvLines({
|
|
107
114
|
...(cdp !== undefined ? { CDP_PORT: cdp } : {}),
|
|
108
115
|
...(port !== undefined ? { WATCHER_PORT: port } : {}),
|
|
109
116
|
...(slotId ? { SLOT_ID: slotId } : {}),
|
|
117
|
+
...(simulator ? { IOS_SIMULATOR: simulator } : {}),
|
|
110
118
|
});
|
|
111
119
|
}
|
|
112
120
|
}
|
|
@@ -142,9 +150,9 @@ export function resolveMobileRuntimeContext(repo) {
|
|
|
142
150
|
if (fs.existsSync(ctxPath)) {
|
|
143
151
|
try {
|
|
144
152
|
const c = JSON.parse(fs.readFileSync(ctxPath, 'utf8'));
|
|
145
|
-
if (c.simulator || c.metroPort) {
|
|
153
|
+
if (c.simulator || (c.metroPort != null && c.metroPort !== '')) {
|
|
146
154
|
return formatKvLines({
|
|
147
|
-
...(c.metroPort !==
|
|
155
|
+
...(c.metroPort != null && c.metroPort !== '' ? { WATCHER_PORT: c.metroPort } : {}),
|
|
148
156
|
...(c.simulator ? { IOS_SIMULATOR: c.simulator } : {}),
|
|
149
157
|
...(c.slotId ? { SLOT_ID: c.slotId } : {}),
|
|
150
158
|
});
|
|
@@ -166,9 +174,10 @@ function resolveMobileProvisionBaseline(repo) {
|
|
|
166
174
|
try {
|
|
167
175
|
const c = JSON.parse(fs.readFileSync(basePath, 'utf8'));
|
|
168
176
|
const simulator = c.simulator?.name || c.simulator?.udid;
|
|
169
|
-
|
|
177
|
+
const watcherPort = c.watcherPort != null && c.watcherPort !== '' ? c.watcherPort : undefined;
|
|
178
|
+
if (!simulator && watcherPort === undefined) return null;
|
|
170
179
|
return formatKvLines({
|
|
171
|
-
...(
|
|
180
|
+
...(watcherPort !== undefined ? { WATCHER_PORT: watcherPort } : {}),
|
|
172
181
|
...(simulator ? { IOS_SIMULATOR: simulator } : {}),
|
|
173
182
|
...(c.slotId ? { SLOT_ID: c.slotId } : {}),
|
|
174
183
|
});
|
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 };
|
|
@@ -14,6 +14,7 @@ function applyKVLines(output, overwrite) {
|
|
|
14
14
|
const m = /^([A-Z_]+)=(.+)$/u.exec(line.trim());
|
|
15
15
|
if (!m) continue;
|
|
16
16
|
const [, key, val] = m;
|
|
17
|
+
if (val === "null" || val === "undefined" || val === "") continue;
|
|
17
18
|
switch (key) {
|
|
18
19
|
case "WATCHER_PORT":
|
|
19
20
|
if (overwrite || !process.env["WATCHER_PORT"]) {
|
|
@@ -39,15 +40,9 @@ function applyKVLines(output, overwrite) {
|
|
|
39
40
|
}
|
|
40
41
|
function resolveMobileSlotPorts(target) {
|
|
41
42
|
const ctxOut = resolveMobileRuntimeContext(target);
|
|
42
|
-
if (ctxOut?.trim())
|
|
43
|
-
applyKVLines(ctxOut, true);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
43
|
+
if (ctxOut?.trim()) applyKVLines(ctxOut, true);
|
|
46
44
|
const poolOut = resolveFarmslotPortsByRepo(target);
|
|
47
|
-
if (poolOut?.trim())
|
|
48
|
-
applyKVLines(poolOut, true);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
45
|
+
if (poolOut?.trim()) applyKVLines(poolOut, false);
|
|
51
46
|
const defOut = resolveMobileSlotDefaults(target);
|
|
52
47
|
if (defOut?.trim()) applyKVLines(defOut, false);
|
|
53
48
|
}
|