@deeeed/metamask-harness 0.17.5 → 0.18.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 +872 -0
- package/README.md +94 -140
- package/adapters/extension/console-tail.mjs +55 -24
- package/adapters/extension/ensure-browser.sh +6 -1
- package/adapters/extension/inject.mjs +6 -9
- package/adapters/extension/launch-browser.cjs +7 -1
- package/adapters/extension/launch-webpack.cjs +83 -0
- package/adapters/extension/launch.sh +8 -11
- package/adapters/extension/lib/chrome-args.cjs +8 -1
- package/adapters/extension/lib/macos-focus.cjs +32 -0
- package/adapters/extension/live.sh +12 -21
- package/adapters/extension/reattach.sh +1 -0
- package/adapters/extension/seed-fixture.sh +4 -12
- package/adapters/extension/sidepanel-toggle.sh +4 -1
- package/adapters/extension/stamp-runtime-title.cjs +58 -0
- package/adapters/extension/start-watch.sh +17 -4
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/sync-webpack-dist.cjs +107 -0
- package/adapters/extension/verify.sh +1 -1
- package/adapters/manifest.json +53 -37
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +16 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +3 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
- package/adapters/mobile/cleanup.sh +16 -176
- package/adapters/mobile/inject.sh +90 -671
- package/adapters/mobile/launch-console-forwarder.cjs +39 -0
- package/adapters/mobile/open-device.sh +45 -7
- package/adapters/mobile/start-console-forwarder.sh +70 -0
- package/adapters/mobile/start-metro.sh +0 -41
- package/adapters/mobile/verify.sh +19 -64
- package/adapters/mobile/wait-for-bridge.sh +22 -8
- package/adapters/shared/resolve-slot-ports-core.mjs +17 -6
- package/adapters/shared/resolve-slot-ports.sh +17 -5
- package/adapters/shared/update-check-worker.mjs +43 -0
- package/bin/mm-harness +21 -2
- package/dist/adapters/core/surface.js +4 -1
- package/dist/adapters/extension/console-capture.js +95 -0
- package/dist/adapters/extension/product-config.js +110 -0
- package/dist/adapters/extension/runtime-decision.js +20 -2
- package/dist/adapters/extension/surface.js +22 -3
- package/dist/adapters/mobile/perps-env.js +43 -3
- package/dist/adapters/mobile/prepare.js +39 -7
- package/dist/adapters/mobile/surface.js +5 -2
- package/dist/adapters/resolve-slot-ports.js +2 -2
- package/dist/adapters/slot-ports.js +13 -16
- package/dist/adapters.js +56 -16
- package/dist/checkout-lock.js +27 -2
- package/dist/cli-color.js +19 -0
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +7 -14
- package/dist/command-contract.js +451 -0
- package/dist/command-journal.js +225 -0
- package/dist/commands/call.js +170 -50
- package/dist/commands/check.js +9 -3
- package/dist/commands/completion-candidates.js +20 -13
- package/dist/commands/device-target.js +27 -12
- package/dist/commands/doctor.js +106 -25
- package/dist/commands/fixtures.js +92 -34
- package/dist/commands/flows.js +39 -10
- package/dist/commands/last.js +52 -0
- package/dist/commands/launch/extension.js +38 -15
- package/dist/commands/launch/index.js +207 -63
- package/dist/commands/list-executables.js +151 -29
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +270 -35
- package/dist/commands/parse-args.js +13 -1
- package/dist/commands/provision.js +10 -3
- package/dist/commands/run-engine.js +435 -105
- package/dist/commands/run-report.js +12 -3
- package/dist/commands/run.js +355 -50
- package/dist/commands/shared.js +75 -3
- package/dist/commands/status-probe.js +4 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/stop.js +7 -2
- package/dist/commands/update.js +16 -28
- package/dist/completions-cache.js +1 -1
- package/dist/doctor.js +57 -10
- package/dist/harness.js +47 -137
- package/dist/heal-bounds.js +2 -2
- package/dist/json-stream.js +57 -0
- package/dist/live-adapter-contract.js +138 -32
- package/dist/manifest.js +161 -1
- package/dist/mm-harness-cli.js +135 -54
- package/dist/paths.js +2 -5
- package/dist/recipe-security.js +178 -0
- package/dist/run-diagnostics.js +261 -0
- package/dist/runner.js +117 -8
- package/docs/CONTRIBUTING.md +137 -0
- package/docs/QA.md +185 -0
- package/docs/RECIPES.md +161 -0
- package/docs/SECURITY.md +88 -0
- package/library/README.md +4 -0
- package/library/actions/core/perps/_controller.mjs +10 -55
- package/library/actions/core/perps/read_account.mjs +2 -2
- package/library/actions/core/perps/read_orders.mjs +2 -1
- package/library/actions/core/perps/read_positions.mjs +2 -1
- package/library/actions/core/wallet/list_accounts.mjs +95 -0
- package/library/actions/extension/platform/cdp.mjs +1 -0
- package/library/actions/extension/wallet/list_accounts.mjs +41 -0
- package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
- package/library/manifests/core.action-manifest.json +68 -7
- package/library/manifests/extension.action-manifest.json +53 -0
- package/library/manifests/mobile.action-manifest.json +81 -3
- package/library/recipes/runner/action-validation.extension.recipe.json +8 -1
- package/library/recipes/runner/action-validation.mobile.recipe.json +8 -1
- package/library/recipes/runner/smoke.core.recipe.json +27 -0
- package/library/recipes/wallet/smoke.extension.recipe.json +42 -0
- package/library/recipes/wallet/smoke.mobile.recipe.json +42 -0
- package/package.json +12 -5
- package/scripts/completions.sh +7 -7
- package/scripts/validate-human-outcomes.mjs +169 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
- package/adapters/mobile/overlay/compat/README.md +0 -28
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
- package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
- package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
- package/adapters/shared/resolve-farmslot-ports.sh +0 -7
- package/docs/ADAPTER-SURFACE.md +0 -119
- package/docs/CHEATSHEET.md +0 -61
- package/docs/CLI-SPEC.md +0 -1098
- package/docs/CODE-MAP.md +0 -62
- package/docs/DEBUG-HANDOVER.md +0 -36
- package/docs/MENTAL-MODEL.md +0 -295
- package/docs/UX-PRINCIPLES.md +0 -64
- package/docs/architecture.md +0 -398
- package/docs/live-adapter-contract.md +0 -188
- package/docs/package-boundaries.md +0 -47
- package/docs/perps-flow-catalog.md +0 -235
- package/docs/recipe-libraries.md +0 -301
- package/docs/runtime-file-conventions.md +0 -36
package/dist/commands/shared.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { detectAdapter } from "../harness.js";
|
|
4
|
+
import { colorHumanMessage } from "../cli-color.js";
|
|
4
5
|
import { resolveLeafInvoke, shellLeafMissing } from "../leaf-invoke.js";
|
|
5
6
|
const EXIT = { ok: 0, runtime: 1, usage: 2, infra: 3, bounded: 4, validation: 5 };
|
|
7
|
+
function writeInteractiveProgress(json, message, {
|
|
8
|
+
stdoutIsTTY = Boolean(process.stdout.isTTY),
|
|
9
|
+
stream = process.stderr
|
|
10
|
+
} = {}) {
|
|
11
|
+
if (json || !stdoutIsTTY) return false;
|
|
12
|
+
stream.write(`${colorHumanMessage(message, { stream })}
|
|
13
|
+
`);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
6
16
|
function parseFlags(argv, booleans) {
|
|
7
17
|
const positional = [];
|
|
8
18
|
const options = {};
|
|
@@ -138,11 +148,19 @@ function spawnScriptStreaming(script, args, cwd, options) {
|
|
|
138
148
|
let settled = false;
|
|
139
149
|
let didTimeout = false;
|
|
140
150
|
let escalation;
|
|
151
|
+
let forwardedParentSignal;
|
|
152
|
+
let parentSignalFallback;
|
|
153
|
+
const parentSignals = ["SIGINT", "SIGTERM"];
|
|
154
|
+
const removeParentSignalHandlers = () => {
|
|
155
|
+
for (const signal of parentSignals) process.removeListener(signal, parentSignalHandlers[signal]);
|
|
156
|
+
};
|
|
141
157
|
const finish = (result) => {
|
|
142
158
|
if (settled) return;
|
|
143
159
|
settled = true;
|
|
144
160
|
if (timer) clearTimeout(timer);
|
|
145
|
-
if (escalation
|
|
161
|
+
if (escalation) clearTimeout(escalation);
|
|
162
|
+
if (parentSignalFallback) clearTimeout(parentSignalFallback);
|
|
163
|
+
removeParentSignalHandlers();
|
|
146
164
|
resolve(result);
|
|
147
165
|
};
|
|
148
166
|
const signalChildTree = (signal) => {
|
|
@@ -152,6 +170,26 @@ function spawnScriptStreaming(script, args, cwd, options) {
|
|
|
152
170
|
} catch {
|
|
153
171
|
}
|
|
154
172
|
};
|
|
173
|
+
const exitFromForwardedSignal = () => {
|
|
174
|
+
if (!forwardedParentSignal) return;
|
|
175
|
+
const signal = forwardedParentSignal;
|
|
176
|
+
forwardedParentSignal = void 0;
|
|
177
|
+
if (parentSignalFallback) clearTimeout(parentSignalFallback);
|
|
178
|
+
process.kill(process.pid, signal);
|
|
179
|
+
};
|
|
180
|
+
const parentSignalHandlers = Object.fromEntries(parentSignals.map((signal) => [signal, () => {
|
|
181
|
+
if (forwardedParentSignal) return;
|
|
182
|
+
forwardedParentSignal = signal;
|
|
183
|
+
signalChildTree(signal);
|
|
184
|
+
removeParentSignalHandlers();
|
|
185
|
+
parentSignalFallback = setTimeout(() => {
|
|
186
|
+
signalChildTree("SIGKILL");
|
|
187
|
+
exitFromForwardedSignal();
|
|
188
|
+
}, 1e3);
|
|
189
|
+
}]));
|
|
190
|
+
if (ownsProcessGroup) {
|
|
191
|
+
for (const signal of parentSignals) process.once(signal, parentSignalHandlers[signal]);
|
|
192
|
+
}
|
|
155
193
|
const timeoutMs = spawnOptions.timeoutMs;
|
|
156
194
|
const timer = Number.isFinite(timeoutMs) && Number(timeoutMs) > 0 ? setTimeout(() => {
|
|
157
195
|
didTimeout = true;
|
|
@@ -182,8 +220,17 @@ function spawnScriptStreaming(script, args, cwd, options) {
|
|
|
182
220
|
`);
|
|
183
221
|
finish({ status: 1, output: message });
|
|
184
222
|
});
|
|
223
|
+
child.on("exit", () => {
|
|
224
|
+
if (forwardedParentSignal) {
|
|
225
|
+
exitFromForwardedSignal();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!didTimeout) return;
|
|
229
|
+
signalChildTree("SIGKILL");
|
|
230
|
+
finish({ status: 1, output, timedOut: true, timeoutMs: Number(timeoutMs) });
|
|
231
|
+
});
|
|
185
232
|
child.on("close", (status) => {
|
|
186
|
-
if (didTimeout
|
|
233
|
+
if (didTimeout) signalChildTree("SIGKILL");
|
|
187
234
|
finish({
|
|
188
235
|
status: didTimeout ? 1 : status ?? 1,
|
|
189
236
|
output,
|
|
@@ -208,9 +255,33 @@ function usageOut(json, command, message, userAction) {
|
|
|
208
255
|
}
|
|
209
256
|
return EXIT.usage;
|
|
210
257
|
}
|
|
258
|
+
function checkoutBusyOut(json, command, message, lockPath) {
|
|
259
|
+
const userAction = `wait for the current owner, or inspect ${lockPath} if its process has exited`;
|
|
260
|
+
if (json) {
|
|
261
|
+
console.log(
|
|
262
|
+
JSON.stringify(
|
|
263
|
+
{
|
|
264
|
+
schemaVersion: 1,
|
|
265
|
+
command,
|
|
266
|
+
status: "fail",
|
|
267
|
+
exitCode: EXIT.bounded,
|
|
268
|
+
recoverable: false,
|
|
269
|
+
error: { code: "SANDBOX_BUSY", message, userAction }
|
|
270
|
+
},
|
|
271
|
+
null,
|
|
272
|
+
2
|
|
273
|
+
)
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
console.error(`\u2717 mm-harness ${command}: ${message}
|
|
277
|
+
Next: ${userAction}`);
|
|
278
|
+
}
|
|
279
|
+
return EXIT.bounded;
|
|
280
|
+
}
|
|
211
281
|
export {
|
|
212
282
|
ADAPTER_DETECT_NEXT,
|
|
213
283
|
EXIT,
|
|
284
|
+
checkoutBusyOut,
|
|
214
285
|
flag,
|
|
215
286
|
parseFlags,
|
|
216
287
|
resolveAdapter,
|
|
@@ -220,5 +291,6 @@ export {
|
|
|
220
291
|
spawnScriptStreaming,
|
|
221
292
|
str,
|
|
222
293
|
targetOf,
|
|
223
|
-
usageOut
|
|
294
|
+
usageOut,
|
|
295
|
+
writeInteractiveProgress
|
|
224
296
|
};
|
|
@@ -22,6 +22,9 @@ function routeToString(route) {
|
|
|
22
22
|
function deriveWalletState(account, route) {
|
|
23
23
|
const routeStr = (routeToString(route) ?? "").toLowerCase();
|
|
24
24
|
if (routeStr.includes("onboard")) return "onboarding";
|
|
25
|
+
if (routeStr === "login" || routeStr === "lockscreen") {
|
|
26
|
+
return "locked";
|
|
27
|
+
}
|
|
25
28
|
if (account !== null && account !== void 0 && typeof account === "object") return "unlocked";
|
|
26
29
|
return "locked";
|
|
27
30
|
}
|
|
@@ -34,7 +37,7 @@ function parseBridgeEntries(raw) {
|
|
|
34
37
|
deviceName: typeof e.deviceName === "string" ? e.deviceName : "",
|
|
35
38
|
platform: typeof e.platform === "string" ? e.platform : "",
|
|
36
39
|
// agenticPresent is only set when the bridge explicitly sends it (false = absent).
|
|
37
|
-
//
|
|
40
|
+
// An omitted value remains unknown.
|
|
38
41
|
...typeof e.agenticPresent === "boolean" ? { agenticPresent: e.agenticPresent } : {}
|
|
39
42
|
}));
|
|
40
43
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -31,7 +31,7 @@ async function handleStatus({ options }) {
|
|
|
31
31
|
if (adapter === "mobile") {
|
|
32
32
|
const dtResult = applyDeviceTargeting("status", adapter, options, { gate: false, rerun: "" });
|
|
33
33
|
if ("code" in dtResult) {
|
|
34
|
-
return usageOut(json, "status", dtResult.message,
|
|
34
|
+
return usageOut(json, "status", dtResult.message, dtResult.userAction);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
const next = surface.hints.relaunch;
|
|
@@ -104,6 +104,7 @@ function renderRuntimeStatus(runtime, out) {
|
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
106
|
function nextForRuntime(launchHint, relaunchHint, runtime) {
|
|
107
|
+
if (runtime.nextAction) return runtime.nextAction;
|
|
107
108
|
if (runtime.decision === "ready") return "mm-harness logs";
|
|
108
109
|
if (runtime.decision === "relaunch") return launchHint;
|
|
109
110
|
return relaunchHint;
|
package/dist/commands/stop.js
CHANGED
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
parseArgs,
|
|
6
6
|
optionFlag,
|
|
7
7
|
optionString,
|
|
8
|
-
resolveAdapter
|
|
8
|
+
resolveAdapter,
|
|
9
|
+
shellQuote
|
|
9
10
|
} from "./parse-args.js";
|
|
10
11
|
async function handleStop(argv) {
|
|
11
12
|
const { options } = parseArgs(argv, "stop");
|
|
@@ -22,6 +23,8 @@ async function handleStop(argv) {
|
|
|
22
23
|
if (stop.kind === "headless") {
|
|
23
24
|
return usageOut(json, "stop", stop.message, stop.userAction);
|
|
24
25
|
}
|
|
26
|
+
const next = adapter === "extension" ? `${surface.hints.launch} --target ${shellQuote(target)}` : void 0;
|
|
27
|
+
const userAction = `mm-harness status --target ${shellQuote(target)} --json`;
|
|
25
28
|
if (json) {
|
|
26
29
|
console.log(
|
|
27
30
|
JSON.stringify(
|
|
@@ -33,7 +36,8 @@ async function handleStop(argv) {
|
|
|
33
36
|
status: stop.status === 0 ? "pass" : "fail",
|
|
34
37
|
...stop.signalled !== void 0 ? { signalled: stop.signalled } : {},
|
|
35
38
|
exitCode: stop.status,
|
|
36
|
-
...stop.output ? { output: stop.output } : {}
|
|
39
|
+
...stop.output ? { output: stop.output } : {},
|
|
40
|
+
...stop.status === 0 ? next ? { next } : {} : { error: { code: "DEV_SERVER_STOP_FAILED", message: stop.summary, userAction } }
|
|
37
41
|
},
|
|
38
42
|
null,
|
|
39
43
|
2
|
|
@@ -43,6 +47,7 @@ async function handleStop(argv) {
|
|
|
43
47
|
if (stop.output) process.stderr.write(`${stop.output}
|
|
44
48
|
`);
|
|
45
49
|
console.error(`${color(stop.status === 0 ? "ok" : "err", stop.status === 0 ? "\u2713" : "\u2717")} ${stop.summary}`);
|
|
50
|
+
if (stop.status !== 0 || next) console.error(` Next: ${stop.status === 0 ? next : userAction}`);
|
|
46
51
|
}
|
|
47
52
|
return stop.status;
|
|
48
53
|
}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
@@ -6,7 +6,8 @@ import { runnerDir } from "../paths.js";
|
|
|
6
6
|
import { EXIT, flag, parseFlags } from "./shared.js";
|
|
7
7
|
const PACKAGE_NAME = "@deeeed/metamask-harness";
|
|
8
8
|
const NUDGE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9
|
-
const NUDGE_FETCH_TIMEOUT_MS =
|
|
9
|
+
const NUDGE_FETCH_TIMEOUT_MS = 2e3;
|
|
10
|
+
const REGISTRY_DIST_TAGS_ENDPOINT = `https://registry.npmjs.org/-/package/${encodeURIComponent(PACKAGE_NAME)}/dist-tags`;
|
|
10
11
|
function currentVersion() {
|
|
11
12
|
try {
|
|
12
13
|
const pkg = JSON.parse(fs.readFileSync(path.join(runnerDir, "package.json"), "utf8"));
|
|
@@ -56,24 +57,6 @@ function fetchLatest(timeoutMs) {
|
|
|
56
57
|
if (!latest) return { error: { kind: "unreachable", message: "npm returned no dist-tag for latest" } };
|
|
57
58
|
return { latest };
|
|
58
59
|
}
|
|
59
|
-
async function fetchLatestAsync(timeoutMs) {
|
|
60
|
-
const controller = new AbortController();
|
|
61
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
62
|
-
try {
|
|
63
|
-
const url = `https://registry.npmjs.org/-/package/${encodeURIComponent(PACKAGE_NAME)}/dist-tags`;
|
|
64
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
65
|
-
if (!res.ok) return { error: { kind: "unreachable", message: `registry returned HTTP ${res.status}` } };
|
|
66
|
-
const data = await res.json();
|
|
67
|
-
const latest = data["latest"];
|
|
68
|
-
if (!latest) return { error: { kind: "unreachable", message: "npm registry returned no latest dist-tag" } };
|
|
69
|
-
return { latest };
|
|
70
|
-
} catch (err) {
|
|
71
|
-
const msg = err.name === "AbortError" ? `registry fetch timed out after ${timeoutMs}ms` : err.message;
|
|
72
|
-
return { error: { kind: "unreachable", message: msg } };
|
|
73
|
-
} finally {
|
|
74
|
-
clearTimeout(timer);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
60
|
function teachFailure(json, current, code, message, userAction) {
|
|
78
61
|
if (json) {
|
|
79
62
|
console.log(
|
|
@@ -211,23 +194,28 @@ function nudgeDisabled() {
|
|
|
211
194
|
function nudgeLine(current, latest) {
|
|
212
195
|
return isNewer(latest, current) ? `mm-harness ${current} \u2192 ${latest} available \xB7 run: mm-harness update` : null;
|
|
213
196
|
}
|
|
214
|
-
|
|
197
|
+
function maybeNudge(now = Date.now()) {
|
|
215
198
|
if (nudgeDisabled()) return;
|
|
216
199
|
const file = cacheFile();
|
|
217
200
|
const cache = readCache(file);
|
|
218
201
|
const current = currentVersion();
|
|
219
|
-
let latest = cache?.latest ?? "";
|
|
220
202
|
process.once("exit", () => {
|
|
221
|
-
const line = nudgeLine(current, latest);
|
|
203
|
+
const line = nudgeLine(current, cache?.latest ?? "");
|
|
222
204
|
if (line) process.stderr.write(`${line}
|
|
223
205
|
`);
|
|
224
206
|
});
|
|
225
207
|
if (!cache || now - cache.lastCheck >= NUDGE_INTERVAL_MS) {
|
|
226
|
-
writeCache(file, { lastCheck: now, latest });
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
208
|
+
writeCache(file, { lastCheck: now, latest: cache?.latest ?? "" });
|
|
209
|
+
const worker = path.join(runnerDir, "adapters", "shared", "update-check-worker.mjs");
|
|
210
|
+
if (!fs.existsSync(worker)) return;
|
|
211
|
+
try {
|
|
212
|
+
const child = spawn(
|
|
213
|
+
process.execPath,
|
|
214
|
+
[worker, file, String(NUDGE_FETCH_TIMEOUT_MS), REGISTRY_DIST_TAGS_ENDPOINT],
|
|
215
|
+
{ detached: true, stdio: "ignore" }
|
|
216
|
+
);
|
|
217
|
+
child.unref();
|
|
218
|
+
} catch {
|
|
231
219
|
}
|
|
232
220
|
}
|
|
233
221
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { recipeRuntimePath } from "./paths.js";
|
|
4
|
-
const COMPLETION_CACHE_VERSION =
|
|
4
|
+
const COMPLETION_CACHE_VERSION = 2;
|
|
5
5
|
const COMPLETION_CACHE_TTL_MS = 6e4;
|
|
6
6
|
function completionCachePath(projectRoot) {
|
|
7
7
|
return recipeRuntimePath(projectRoot, ".completion-cache.json");
|
package/dist/doctor.js
CHANGED
|
@@ -93,10 +93,7 @@ function fixtureFileSummary(file, adapter, reportedPath = file) {
|
|
|
93
93
|
const RUNTIME_CONTEXT_FIELDS = [
|
|
94
94
|
{ key: "slotId", envVars: ["RECIPE_SLOT_ID"], envVar: "RECIPE_SLOT_ID", customize: "doctor --fix creates local identity; Farmslot may override", adapters: ["mobile", "extension", "core"] },
|
|
95
95
|
{ key: "extensionId", envVars: ["RECIPE_HARNESS_EXTENSION_ID"], envVar: "RECIPE_HARNESS_EXTENSION_ID", customize: "resolved from the built extension", adapters: ["extension"] },
|
|
96
|
-
{ key: "cdpPort", envVars: ["RECIPE_CDP_PORT", "CDP_PORT"], envVar: "CDP_PORT", customize: "doctor --fix claims one; --cdp-port overrides", adapters: ["extension"] }
|
|
97
|
-
{ key: "runtimeStart.approved", envVars: ["RECIPE_RUNTIME_START_APPROVED"], envVar: "RECIPE_RUNTIME_START_APPROVED", customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] },
|
|
98
|
-
{ key: "runtimeStart.command", envVars: [], envVar: null, customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] },
|
|
99
|
-
{ key: "runtimeStart.readyUrl", envVars: ["RECIPE_RUNTIME_READY_URL"], envVar: "RECIPE_RUNTIME_READY_URL", customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] }
|
|
96
|
+
{ key: "cdpPort", envVars: ["RECIPE_CDP_PORT", "CDP_PORT"], envVar: "CDP_PORT", customize: "doctor --fix claims one; --cdp-port overrides", adapters: ["extension"] }
|
|
100
97
|
];
|
|
101
98
|
function runtimeContextSummary(target, adapter) {
|
|
102
99
|
const contextPath = resolveRuntimeContextPath(target);
|
|
@@ -158,12 +155,7 @@ function createDoctorReport(adapter, target, manifestValidation, actionManifestP
|
|
|
158
155
|
requiredChecks,
|
|
159
156
|
adapter,
|
|
160
157
|
target,
|
|
161
|
-
runner:
|
|
162
|
-
name: "@metamask/recipe-runner",
|
|
163
|
-
runnerDir,
|
|
164
|
-
actionManifestPath,
|
|
165
|
-
harnessPackage: "@farmslot/recipe-harness"
|
|
166
|
-
},
|
|
158
|
+
runner: runnerProvenance(actionManifestPath),
|
|
167
159
|
compatibilityMode: mode,
|
|
168
160
|
shape: repoShape(target),
|
|
169
161
|
fixture: fixtureSummary(target, adapter),
|
|
@@ -172,6 +164,60 @@ function createDoctorReport(adapter, target, manifestValidation, actionManifestP
|
|
|
172
164
|
manifestValidation: manifestValidation.summary
|
|
173
165
|
};
|
|
174
166
|
}
|
|
167
|
+
function runnerInstallKind(runnerRoot, invokedPath, executablePath) {
|
|
168
|
+
const normalizedRoot = path.normalize(runnerRoot);
|
|
169
|
+
const nodeModulesSegment = `${path.sep}node_modules${path.sep}`;
|
|
170
|
+
const globalNodeModulesSegment = `${path.sep}lib${path.sep}node_modules${path.sep}`;
|
|
171
|
+
if (normalizedRoot.includes(globalNodeModulesSegment)) return "global-install";
|
|
172
|
+
const nodeModulesIndex = normalizedRoot.lastIndexOf(nodeModulesSegment);
|
|
173
|
+
if (nodeModulesIndex >= 0) {
|
|
174
|
+
const nodeModulesRoot = normalizedRoot.slice(
|
|
175
|
+
0,
|
|
176
|
+
nodeModulesIndex + nodeModulesSegment.length - 1
|
|
177
|
+
);
|
|
178
|
+
const invoked = path.resolve(invokedPath);
|
|
179
|
+
const projectBin = `${path.join(nodeModulesRoot, ".bin")}${path.sep}`;
|
|
180
|
+
if (invoked === path.resolve(executablePath) || invoked.startsWith(projectBin)) {
|
|
181
|
+
return "project-install";
|
|
182
|
+
}
|
|
183
|
+
return "global-install";
|
|
184
|
+
}
|
|
185
|
+
if (path.resolve(invokedPath) !== path.resolve(executablePath)) return "local-link";
|
|
186
|
+
return "source-checkout";
|
|
187
|
+
}
|
|
188
|
+
function runnerProvenance(actionManifestPath) {
|
|
189
|
+
const packagePath = path.join(runnerDir, "package.json");
|
|
190
|
+
const packageDocument = readJsonObject(packagePath);
|
|
191
|
+
const packageName = packageDocument.name;
|
|
192
|
+
const version = packageDocument.version;
|
|
193
|
+
if (packageName !== "@deeeed/metamask-harness") {
|
|
194
|
+
throw new Error(`Expected @deeeed/metamask-harness package name in ${packagePath}`);
|
|
195
|
+
}
|
|
196
|
+
if (typeof version !== "string" || !version) {
|
|
197
|
+
throw new Error(`Expected package version in ${packagePath}`);
|
|
198
|
+
}
|
|
199
|
+
const executablePath = path.resolve(
|
|
200
|
+
process.env.MM_HARNESS_EXECUTABLE ?? path.join(runnerDir, "bin/mm-harness")
|
|
201
|
+
);
|
|
202
|
+
const invokedPath = path.resolve(process.env.MM_HARNESS_INVOKED_AS ?? executablePath);
|
|
203
|
+
const installKind = runnerInstallKind(runnerDir, invokedPath, executablePath);
|
|
204
|
+
const packageSource = runnerDir.includes(`${path.sep}node_modules${path.sep}`) ? "node_modules" : "source-checkout";
|
|
205
|
+
return {
|
|
206
|
+
name: "@metamask/recipe-runner",
|
|
207
|
+
packageName,
|
|
208
|
+
version,
|
|
209
|
+
runnerDir,
|
|
210
|
+
packagePath,
|
|
211
|
+
packageSource,
|
|
212
|
+
installKind,
|
|
213
|
+
linked: installKind === "local-link",
|
|
214
|
+
global: installKind === "global-install",
|
|
215
|
+
invokedPath,
|
|
216
|
+
executablePath,
|
|
217
|
+
actionManifestPath,
|
|
218
|
+
harnessPackage: "@farmslot/recipe-harness"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
175
221
|
function readJsonObject(file) {
|
|
176
222
|
const value = readJson(file);
|
|
177
223
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -187,5 +233,6 @@ export {
|
|
|
187
233
|
renderRuntimeContext,
|
|
188
234
|
repoShape,
|
|
189
235
|
requiredDoctorCheckSummary,
|
|
236
|
+
runnerInstallKind,
|
|
190
237
|
runtimeContextSummary
|
|
191
238
|
};
|