@deeeed/metamask-harness 0.6.2 → 0.7.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 +77 -0
- package/adapters/extension/inject.mjs +7 -0
- package/adapters/extension/reattach.sh +210 -0
- package/adapters/extension/start-watch.sh +11 -17
- package/adapters/manifest.json +65 -9
- package/adapters/mobile/lib/tmux-viewer.sh +31 -10
- package/adapters/mobile/open-device.sh +14 -1
- package/adapters/mobile/stop-metro.sh +1 -1
- package/adapters/mobile/yarn-setup.sh +15 -1
- package/adapters/shared/activate-repo-ruby.sh +124 -0
- package/adapters/shared/open-log-window.sh +55 -0
- package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -205
- package/adapters/shared/resolve-farmslot-ports.mjs +4 -19
- package/adapters/shared/resolve-farmslot-ports.sh +6 -104
- package/adapters/shared/resolve-slot-ports-core.mjs +213 -0
- package/adapters/shared/resolve-slot-ports.mjs +20 -0
- package/adapters/shared/resolve-slot-ports.sh +110 -0
- package/adapters/shared/tmux-session.sh +35 -0
- package/dist/adapters/extension/runtime-decision.js +4 -0
- package/dist/adapters/mobile/provision.js +25 -2
- package/dist/adapters/{resolve-farmslot-ports.js → resolve-slot-ports.js} +4 -2
- package/dist/adapters/slot-ports.js +8 -10
- package/dist/cli-commands.js +1 -1
- package/dist/commands/call.js +5 -0
- package/dist/commands/doctor.js +21 -1
- package/dist/commands/fixtures.js +5 -3
- package/dist/commands/launch/extension.js +80 -6
- package/dist/commands/launch/index.js +10 -1
- package/dist/commands/list-executables.js +48 -0
- package/dist/commands/logs.js +25 -2
- package/dist/commands/manifest.js +27 -7
- package/dist/commands/parse-args.js +1 -0
- package/dist/commands/run.js +3 -4
- package/dist/live-adapter-contract.js +30 -9
- package/dist/mm-harness-cli.js +3 -1
- package/dist/paths.js +4 -1
- package/package.json +1 -1
|
@@ -1,17 +1,88 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import http from "node:http";
|
|
1
3
|
import fs from "node:fs";
|
|
2
4
|
import path from "node:path";
|
|
5
|
+
import { depsCheck } from "@farmslot/recipe-harness/runtime/deps-readiness";
|
|
3
6
|
import { recipeHarnessPath, recipeRuntimeDir, runnerDir } from "../../paths.js";
|
|
7
|
+
import { isExtensionDistStale } from "../../adapters/extension/runtime-decision.js";
|
|
4
8
|
import { stopExtensionWatcher } from "../../adapters/slot-ports.js";
|
|
5
9
|
import { spawnScriptStreaming } from "../shared.js";
|
|
10
|
+
function extensionDepsBlock(target) {
|
|
11
|
+
if (!fs.existsSync(path.join(target, "package.json"))) return null;
|
|
12
|
+
const deps = depsCheck(target);
|
|
13
|
+
const userAction = "yarn install --immutable # then re-run: mm-harness launch";
|
|
14
|
+
if (deps.status === "missing") {
|
|
15
|
+
return { message: "extension dependencies are not installed (no yarn install-state markers).", userAction };
|
|
16
|
+
}
|
|
17
|
+
if (deps.status === "stale") {
|
|
18
|
+
return {
|
|
19
|
+
message: "extension dependencies are stale (package.json/yarn.lock changed since the last install) \u2014 webpack would crash mid-build.",
|
|
20
|
+
userAction
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
6
25
|
async function launchExtension(target, tier, wantWatch) {
|
|
7
|
-
if (
|
|
26
|
+
if (wantWatch) {
|
|
27
|
+
const startWatchSh = path.join(runnerDir, "adapters/extension/start-watch.sh");
|
|
28
|
+
const watchArgs = ["--target", target];
|
|
29
|
+
if (process.env.WATCHER_PORT) watchArgs.push("--watcher-port", process.env.WATCHER_PORT);
|
|
30
|
+
console.error(`\u2192 extension watch \u2014 webpack :${process.env.WATCHER_PORT ?? "default"} (output streams below)`);
|
|
31
|
+
return spawnScriptStreaming(startWatchSh, watchArgs, target);
|
|
32
|
+
}
|
|
33
|
+
if (tier === "build") {
|
|
8
34
|
return extensionRebuild(target);
|
|
9
35
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
36
|
+
if (await extensionRuntimeReusable(target)) {
|
|
37
|
+
return extensionReattach(target);
|
|
38
|
+
}
|
|
39
|
+
return extensionRebuild(target);
|
|
40
|
+
}
|
|
41
|
+
async function extensionRuntimeReusable(target) {
|
|
42
|
+
const watcherPort = process.env.WATCHER_PORT;
|
|
43
|
+
const cdpPort = process.env.CDP_PORT;
|
|
44
|
+
if (!watcherPort || !cdpPort) return false;
|
|
45
|
+
if (!portHasListener(watcherPort)) return false;
|
|
46
|
+
if (!await cdpVersionReachable(cdpPort)) return false;
|
|
47
|
+
if (isExtensionDistStale(target)) return false;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function portHasListener(port) {
|
|
51
|
+
try {
|
|
52
|
+
const out = execFileSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
55
|
+
});
|
|
56
|
+
return out.split(/\s+/u).some((value) => /^\d+$/u.test(value));
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function cdpVersionReachable(port) {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
const request = http.get(
|
|
64
|
+
{ host: "127.0.0.1", port: Number(port), path: "/json/version", timeout: 2e3 },
|
|
65
|
+
(response) => {
|
|
66
|
+
response.resume();
|
|
67
|
+
const code = response.statusCode ?? 0;
|
|
68
|
+
resolve(code >= 200 && code < 300);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
request.on("timeout", () => {
|
|
72
|
+
request.destroy();
|
|
73
|
+
resolve(false);
|
|
74
|
+
});
|
|
75
|
+
request.on("error", () => resolve(false));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function extensionReattach(target) {
|
|
79
|
+
const reattachScript = recipeHarnessPath(target, "extension", "scripts", "reattach.sh");
|
|
80
|
+
const reattachArgs = ["--target", target];
|
|
81
|
+
if (process.env.CDP_PORT) reattachArgs.push("--cdp-port", process.env.CDP_PORT);
|
|
82
|
+
if (process.env.WATCHER_PORT) reattachArgs.push("--watcher-port", process.env.WATCHER_PORT);
|
|
83
|
+
if (process.env.EXTENSION_START_URL) reattachArgs.push("--start-url", process.env.EXTENSION_START_URL);
|
|
84
|
+
console.error(`\u2192 extension quick reattach \u2014 reload in place \xB7 CDP :${process.env.CDP_PORT ?? "default"} (no rebuild, no relaunch)`);
|
|
85
|
+
return spawnScriptStreaming(reattachScript, reattachArgs, target);
|
|
15
86
|
}
|
|
16
87
|
async function extensionRebuild(target) {
|
|
17
88
|
const runtimeDirRel = recipeRuntimeDir();
|
|
@@ -35,6 +106,9 @@ async function extensionRebuild(target) {
|
|
|
35
106
|
return result;
|
|
36
107
|
}
|
|
37
108
|
export {
|
|
109
|
+
extensionDepsBlock,
|
|
110
|
+
extensionReattach,
|
|
38
111
|
extensionRebuild,
|
|
112
|
+
extensionRuntimeReusable,
|
|
39
113
|
launchExtension
|
|
40
114
|
};
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
newHealState,
|
|
24
24
|
parseHeal
|
|
25
25
|
} from "../../heal-bounds.js";
|
|
26
|
-
import { launchExtension } from "./extension.js";
|
|
26
|
+
import { extensionDepsBlock, extensionRuntimeReusable, launchExtension } from "./extension.js";
|
|
27
27
|
import { launchMobile } from "./mobile.js";
|
|
28
28
|
const LAUNCH_BOOLEANS = /* @__PURE__ */ new Set([
|
|
29
29
|
"build",
|
|
@@ -111,6 +111,15 @@ async function handleLaunch(argv) {
|
|
|
111
111
|
if (json) liveArgs.push("--json");
|
|
112
112
|
return handleHarness(liveArgs);
|
|
113
113
|
}
|
|
114
|
+
if (adapter === "extension") {
|
|
115
|
+
const willBuild = wantWatch || tier === "build" || !await extensionRuntimeReusable(target);
|
|
116
|
+
if (willBuild) {
|
|
117
|
+
const depsBlock = extensionDepsBlock(target);
|
|
118
|
+
if (depsBlock) {
|
|
119
|
+
return usageOut(json, "launch", depsBlock.message, depsBlock.userAction);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
114
123
|
let attempt = await executeComposition(adapter, mobileTarget, tier, wantWatch, target, json);
|
|
115
124
|
if (attempt.status === 0) {
|
|
116
125
|
return finishLaunch(json, adapter, mobileTarget, tier, displayMode, target, state);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadActionManifest } from "../manifest.js";
|
|
2
|
+
import { importRecipeHarness, importRecipeProtocol } from "../paths.js";
|
|
3
|
+
import { EXIT } from "./shared.js";
|
|
4
|
+
import { optionFlag, optionString, resolveAdapter } from "./parse-args.js";
|
|
5
|
+
import { resolveMetaMaskLibrarySources } from "./run-engine.js";
|
|
6
|
+
const finalSegment = (name) => name.split(".").pop() ?? name;
|
|
7
|
+
async function listFlowIds() {
|
|
8
|
+
try {
|
|
9
|
+
const sources = await resolveMetaMaskLibrarySources(void 0);
|
|
10
|
+
if (!sources || sources.length === 0) return [];
|
|
11
|
+
const harness = await importRecipeHarness();
|
|
12
|
+
const resolution = await harness.loadRecipeLibraries(sources);
|
|
13
|
+
return [...resolution.flows.keys()].sort();
|
|
14
|
+
} catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function handleListExecutables(command, options) {
|
|
19
|
+
const json = optionFlag(options, "json");
|
|
20
|
+
const { adapter } = resolveAdapter(options);
|
|
21
|
+
const manifest = loadActionManifest(adapter, optionString(options, "actionManifest"));
|
|
22
|
+
const { getRecipeActionManifestActionNames } = await importRecipeProtocol();
|
|
23
|
+
const names = getRecipeActionManifestActionNames(manifest).slice().sort();
|
|
24
|
+
const shortCounts = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const name of names) shortCounts.set(finalSegment(name), (shortCounts.get(finalSegment(name)) ?? 0) + 1);
|
|
26
|
+
const actions = names.map((name) => {
|
|
27
|
+
const short = finalSegment(name);
|
|
28
|
+
return { name, short: shortCounts.get(short) === 1 ? short : null };
|
|
29
|
+
});
|
|
30
|
+
const flows = await listFlowIds();
|
|
31
|
+
if (json) {
|
|
32
|
+
console.log(JSON.stringify({ schemaVersion: 1, command, action: "list", adapter, actions, flows }, null, 2));
|
|
33
|
+
return EXIT.ok;
|
|
34
|
+
}
|
|
35
|
+
console.log(`invocable for ${adapter}:`);
|
|
36
|
+
console.log(` actions (mm-harness call <name> \u2014 short or full):`);
|
|
37
|
+
for (const entry of actions) {
|
|
38
|
+
console.log(entry.short ? ` ${entry.short} (${entry.name})` : ` ${entry.name} (full name only \u2014 ambiguous short)`);
|
|
39
|
+
}
|
|
40
|
+
if (flows.length > 0) {
|
|
41
|
+
console.log(` flows (mm-harness run <flow>):`);
|
|
42
|
+
for (const flow of flows) console.log(` ${flow}`);
|
|
43
|
+
}
|
|
44
|
+
return EXIT.ok;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
handleListExecutables
|
|
48
|
+
};
|
package/dist/commands/logs.js
CHANGED
|
@@ -2,8 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { runnerDir } from "../paths.js";
|
|
4
4
|
import { getAdapterSurface } from "../adapters/surface.js";
|
|
5
|
-
import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnInherit, str, targetOf, usageOut } from "./shared.js";
|
|
6
|
-
const LOGS_BOOLEANS = /* @__PURE__ */ new Set(["full", "json"]);
|
|
5
|
+
import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnInherit, spawnScript, str, targetOf, usageOut } from "./shared.js";
|
|
6
|
+
const LOGS_BOOLEANS = /* @__PURE__ */ new Set(["full", "json", "window"]);
|
|
7
7
|
async function handleLogs(argv) {
|
|
8
8
|
const { options } = parseFlags(argv, LOGS_BOOLEANS);
|
|
9
9
|
const json = flag(options, "json");
|
|
@@ -43,6 +43,29 @@ async function handleLogs(argv) {
|
|
|
43
43
|
surface.hints.launch
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
|
+
if (flag(options, "window")) {
|
|
47
|
+
surface.resolveSlotPorts(target);
|
|
48
|
+
const port = process.env.WATCHER_PORT ?? process.env.METRO_PORT ?? "default";
|
|
49
|
+
const windowName = adapter === "mobile" ? `metro-${port}` : `webpack-${port}`;
|
|
50
|
+
const devLog = logSources[0].path;
|
|
51
|
+
const leaf = path.join(runnerDir, "adapters/shared/open-log-window.sh");
|
|
52
|
+
const result = spawnScript(
|
|
53
|
+
leaf,
|
|
54
|
+
["--window", windowName, "--log", devLog, "--runtime-dir", path.dirname(devLog)],
|
|
55
|
+
target,
|
|
56
|
+
json
|
|
57
|
+
);
|
|
58
|
+
if (json) {
|
|
59
|
+
console.log(
|
|
60
|
+
JSON.stringify(
|
|
61
|
+
{ schemaVersion: 1, command: "logs", action: "window", adapter, window: windowName, logFile: devLog, exitCode: result.status === 0 ? EXIT.ok : EXIT.runtime },
|
|
62
|
+
null,
|
|
63
|
+
2
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return result.status === 0 ? EXIT.ok : EXIT.runtime;
|
|
68
|
+
}
|
|
46
69
|
if (json) {
|
|
47
70
|
console.log(JSON.stringify({ schemaVersion: 1, command: "logs", adapter, source, logFile, exitCode: EXIT.ok }, null, 2));
|
|
48
71
|
return EXIT.ok;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadActionManifest, validateManifest } from "../manifest.js";
|
|
2
|
+
import { EXIT } from "./shared.js";
|
|
2
3
|
import {
|
|
3
4
|
actionManifestPathOption,
|
|
4
5
|
isRecord,
|
|
@@ -19,9 +20,22 @@ async function handleActions({ options }) {
|
|
|
19
20
|
const { adapter } = resolveAdapter(options);
|
|
20
21
|
const manifest = loadActionManifest(adapter, optionString(options, "actionManifest"));
|
|
21
22
|
await validateManifest(manifest);
|
|
23
|
+
const json = optionFlag(options, "json");
|
|
22
24
|
const action = optionString(options, "action");
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
+
const all = describeManifestActions(manifest);
|
|
26
|
+
const actions = action ? fuzzyResolveActions(all, action) : all;
|
|
27
|
+
if (action && actions.length === 0) {
|
|
28
|
+
const message = `no action matches "${action}" for the ${adapter} adapter.`;
|
|
29
|
+
const userAction = `mm-harness actions --adapter ${adapter} # list the vocabulary`;
|
|
30
|
+
if (json) {
|
|
31
|
+
console.log(JSON.stringify({ schemaVersion: 1, command: "actions", adapter, action, error: { code: "ACTION_UNKNOWN", message, userAction } }, null, 2));
|
|
32
|
+
} else {
|
|
33
|
+
console.error(`\u2717 mm-harness actions: ${message}
|
|
34
|
+
Next: ${userAction}`);
|
|
35
|
+
}
|
|
36
|
+
return EXIT.usage;
|
|
37
|
+
}
|
|
38
|
+
if (json) {
|
|
25
39
|
console.log(JSON.stringify({ adapter, actions }, null, 2));
|
|
26
40
|
} else {
|
|
27
41
|
for (const entry of actions) {
|
|
@@ -31,7 +45,15 @@ async function handleActions({ options }) {
|
|
|
31
45
|
}
|
|
32
46
|
return 0;
|
|
33
47
|
}
|
|
34
|
-
function
|
|
48
|
+
function fuzzyResolveActions(entries, query) {
|
|
49
|
+
const exactFull = entries.filter((e) => e.name === query);
|
|
50
|
+
if (exactFull.length > 0) return exactFull;
|
|
51
|
+
const finalSegment = (name) => name.split(".").pop() ?? name;
|
|
52
|
+
const exactSegment = entries.filter((e) => finalSegment(e.name) === query);
|
|
53
|
+
if (exactSegment.length > 0) return exactSegment;
|
|
54
|
+
return entries.filter((e) => finalSegment(e.name).includes(query));
|
|
55
|
+
}
|
|
56
|
+
function describeManifestActions(manifest) {
|
|
35
57
|
const manifestRecord = isRecord(manifest) ? manifest : {};
|
|
36
58
|
const metadata = isRecord(manifestRecord.action_metadata) ? manifestRecord.action_metadata : {};
|
|
37
59
|
const official = Array.isArray(manifestRecord.supported_official_actions) ? manifestRecord.supported_official_actions.filter((value) => typeof value === "string") : [];
|
|
@@ -45,12 +67,10 @@ function describeManifestActions(manifest, filterAction) {
|
|
|
45
67
|
}
|
|
46
68
|
return [];
|
|
47
69
|
}) : [];
|
|
48
|
-
|
|
70
|
+
return [
|
|
49
71
|
...official.map((name) => describeManifestAction(name, "official", metadata[name])),
|
|
50
72
|
...custom.map((entry) => describeManifestAction(entry.name, "custom", entry.metadata))
|
|
51
|
-
]
|
|
52
|
-
if (filterAction && entries.length === 0) throw new Error(`Action not found in manifest: ${filterAction}`);
|
|
53
|
-
return entries;
|
|
73
|
+
];
|
|
54
74
|
}
|
|
55
75
|
function describeManifestAction(name, kind, metadata) {
|
|
56
76
|
const record = isRecord(metadata) ? metadata : {};
|
package/dist/commands/run.js
CHANGED
|
@@ -3,13 +3,11 @@ import path from "node:path";
|
|
|
3
3
|
import { walletFixturePath } from "../paths.js";
|
|
4
4
|
import { EXIT } from "./shared.js";
|
|
5
5
|
import {
|
|
6
|
-
adapterOption,
|
|
7
6
|
optionFlag,
|
|
8
7
|
optionString,
|
|
9
8
|
requiredOption,
|
|
10
9
|
resolveAdapter,
|
|
11
10
|
runtimeOptionsFromCli,
|
|
12
|
-
targetPath,
|
|
13
11
|
usageError
|
|
14
12
|
} from "./parse-args.js";
|
|
15
13
|
import {
|
|
@@ -21,13 +19,14 @@ import {
|
|
|
21
19
|
runRecipe,
|
|
22
20
|
validateRunRecipeStatic
|
|
23
21
|
} from "./run-engine.js";
|
|
22
|
+
import { handleListExecutables } from "./list-executables.js";
|
|
24
23
|
async function handleRun({ positional, options }) {
|
|
24
|
+
if (optionFlag(options, "list")) return handleListExecutables("run", options);
|
|
25
25
|
const targetRecipe = positional[0];
|
|
26
26
|
if (!targetRecipe) throw usageError("run requires <recipe.json>.");
|
|
27
27
|
if (optionFlag(options, "plan")) return handleRunPlan(targetRecipe, options);
|
|
28
|
-
const adapter =
|
|
28
|
+
const { adapter, target } = resolveAdapter(options);
|
|
29
29
|
const json = optionFlag(options, "json");
|
|
30
|
-
const target = targetPath(options);
|
|
31
30
|
const artifactsDir = requiredOption(options, "artifactsDir", "run requires --artifacts-dir <dir>.");
|
|
32
31
|
const prepared = await prepareHeal(adapter, target, options, json);
|
|
33
32
|
if (typeof prepared === "number") return prepared;
|
|
@@ -119,18 +119,39 @@ function runProcess(command, args, options) {
|
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
|
-
function commandFor(file) {
|
|
122
|
+
function commandFor(file, projectRoot, platform) {
|
|
123
123
|
if (file.endsWith(".sh")) return { command: "bash", args: [file] };
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
const needsTsx = platform === "core" || importsSourceTypescript(file);
|
|
125
|
+
if (!needsTsx && (file.endsWith(".mjs") || file.endsWith(".js"))) {
|
|
126
|
+
return { command: process.execPath, args: [file] };
|
|
127
|
+
}
|
|
128
|
+
const tsxBin = resolveTsxBin(projectRoot);
|
|
129
|
+
if (!tsxBin) {
|
|
130
|
+
const where = projectRoot ?? "the checkout";
|
|
131
|
+
const error = new Error(
|
|
132
|
+
`this action runs TypeScript from the checkout but no tsx runtime was found.
|
|
133
|
+
Next: run 'yarn install' in ${where} (tsx is a dev dependency of the checkout); if package imports still fail, run 'yarn build' there`
|
|
134
|
+
);
|
|
135
|
+
error.exitCode = 2;
|
|
136
|
+
throw error;
|
|
126
137
|
}
|
|
127
|
-
const localTsx = path.join(runnerDir, "node_modules/.bin/tsx");
|
|
128
|
-
const tsxBin = process.env.TSX_BIN || (existsSync(localTsx) ? localTsx : path.join(
|
|
129
|
-
resolveRequiredLocalProtocolRoot("TypeScript live adapter execution"),
|
|
130
|
-
"node_modules/.bin/tsx"
|
|
131
|
-
));
|
|
132
138
|
return { command: tsxBin, args: [file] };
|
|
133
139
|
}
|
|
140
|
+
function resolveTsxBin(projectRoot) {
|
|
141
|
+
if (process.env.TSX_BIN) return process.env.TSX_BIN;
|
|
142
|
+
const candidates = [];
|
|
143
|
+
if (projectRoot) candidates.push(path.join(projectRoot, "node_modules/.bin/tsx"));
|
|
144
|
+
candidates.push(path.join(runnerDir, "node_modules/.bin/tsx"));
|
|
145
|
+
for (const candidate of candidates) {
|
|
146
|
+
if (existsSync(candidate)) return candidate;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const protocolTsx = path.join(resolveRequiredLocalProtocolRoot("TypeScript live adapter execution"), "node_modules/.bin/tsx");
|
|
150
|
+
if (existsSync(protocolTsx)) return protocolTsx;
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
134
155
|
function importsSourceTypescript(file) {
|
|
135
156
|
return importsSourceTypescriptFrom(file, /* @__PURE__ */ new Set());
|
|
136
157
|
}
|
|
@@ -201,7 +222,7 @@ async function runLiveAdapterScript({ platform, action, node, context }) {
|
|
|
201
222
|
};
|
|
202
223
|
await writeFile(inputPath, `${JSON.stringify(input, null, 2)}
|
|
203
224
|
`);
|
|
204
|
-
const command = commandFor(script);
|
|
225
|
+
const command = commandFor(script, context.projectRoot, platform);
|
|
205
226
|
const platformEnv = await platformAdapterEnv(platform, context.projectRoot, tempDir);
|
|
206
227
|
const result = await runProcess(command.command, [...command.args, inputPath], {
|
|
207
228
|
cwd: context.projectRoot,
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -230,8 +230,10 @@ Example:
|
|
|
230
230
|
Launch the app \u2014 quick relaunch by default; --build for a full native/webpack build.
|
|
231
231
|
Mobile: target is MANDATORY (ios | android). Extension: no target (--fullscreen default;
|
|
232
232
|
--sidepanel to override). core is headless (teaching error \u2014 use verify/run).
|
|
233
|
+
Extension quick launch REUSES a healthy live runtime (reload in place, no rebuild);
|
|
234
|
+
--build is the escape hatch that always clean-builds + relaunches a fresh runtime.
|
|
233
235
|
|
|
234
|
-
--build Full
|
|
236
|
+
--build Full clean build + relaunch (escape hatch; skips extension reuse)
|
|
235
237
|
--verify Launch then poll CDP/bridge until ready (absorbs the old \`live\`)
|
|
236
238
|
--runway Post-launch runway check (mobile only; teaching error elsewhere)
|
|
237
239
|
--watch Persistent webpack watcher then relaunch (extension only)
|
package/dist/paths.js
CHANGED
|
@@ -47,6 +47,9 @@ function recipeWatchLogCandidates() {
|
|
|
47
47
|
}
|
|
48
48
|
function resolveLocalProtocolRoot() {
|
|
49
49
|
const candidates = [
|
|
50
|
+
// Orchestrator-neutral name is primary; FARMSLOT_ROOT stays a back-compat alias
|
|
51
|
+
// for one release (the installer injects METAMASK_RUNNER_PROTOCOL_ROOT).
|
|
52
|
+
process.env.METAMASK_RUNNER_PROTOCOL_ROOT,
|
|
50
53
|
process.env.FARMSLOT_ROOT,
|
|
51
54
|
readConfiguredProtocolRoot(),
|
|
52
55
|
findProtocolRoot(runnerDir),
|
|
@@ -59,7 +62,7 @@ function resolveRequiredLocalProtocolRoot(reason) {
|
|
|
59
62
|
const root = resolveLocalProtocolRoot();
|
|
60
63
|
if (!root) {
|
|
61
64
|
throw new Error(
|
|
62
|
-
`${reason} requires a local protocol/runtime checkout. Set FARMSLOT_ROOT or create .farmslot-root for this dev-only path.`
|
|
65
|
+
`${reason} requires a local protocol/runtime checkout. Set METAMASK_RUNNER_PROTOCOL_ROOT (or the legacy FARMSLOT_ROOT), or create .farmslot-root for this dev-only path.`
|
|
63
66
|
);
|
|
64
67
|
}
|
|
65
68
|
return root;
|