@deeeed/metamask-harness 0.74.0 → 0.76.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 +39 -1
- package/README.md +1 -1
- package/adapters/shared/ensure-runner-deps.sh +15 -3
- package/adapters/shared/reap-checkout-metros.sh +9 -0
- package/dist/action-values.js +15 -0
- package/dist/adapters/core/actions.js +20 -0
- package/dist/adapters/core/live-script-env.js +36 -0
- package/dist/adapters/core/overlay.js +34 -0
- package/dist/adapters/core/readiness.js +8 -0
- package/dist/adapters/core/run.js +54 -0
- package/dist/adapters/core/surface.js +12 -1
- package/dist/adapters/extension/action-inputs.js +206 -0
- package/dist/adapters/extension/actions.js +51 -0
- package/dist/adapters/extension/console-capture.js +13 -1
- package/dist/adapters/extension/doctor-checks.js +135 -0
- package/dist/adapters/extension/fixtures.js +103 -0
- package/dist/adapters/extension/launch.js +315 -0
- package/dist/adapters/extension/launched-browser.js +51 -0
- package/dist/adapters/extension/overlay.js +55 -0
- package/dist/adapters/extension/readiness.js +32 -0
- package/dist/adapters/extension/reload.js +40 -0
- package/dist/adapters/extension/run.js +105 -0
- package/dist/adapters/extension/surface.js +31 -3
- package/dist/adapters/harness-freshness.js +4 -2
- package/dist/adapters/mobile/actions.js +54 -0
- package/dist/adapters/mobile/devices.js +21 -0
- package/dist/adapters/mobile/doctor-checks.js +40 -0
- package/dist/adapters/mobile/fixtures.js +141 -0
- package/dist/adapters/mobile/launch.js +192 -0
- package/dist/adapters/mobile/lifecycle.js +300 -0
- package/dist/adapters/mobile/overlay.js +27 -0
- package/dist/adapters/mobile/prepare-target.js +38 -0
- package/dist/adapters/mobile/readiness.js +39 -0
- package/dist/adapters/mobile/reload.js +15 -0
- package/dist/adapters/mobile/run.js +156 -0
- package/dist/adapters/mobile/runtime-identity.js +34 -0
- package/dist/adapters/mobile/surface.js +39 -3
- package/dist/adapters/mobile/ui.js +701 -0
- package/dist/adapters/performance/cdp-trace.js +24 -6
- package/dist/adapters/performance/device-platform.js +10 -0
- package/dist/adapters/registry.js +1 -1
- package/dist/adapters/terminal/actions.js +43 -0
- package/dist/adapters/terminal/console-capture.js +14 -1
- package/dist/adapters/terminal/doctor-checks.js +22 -0
- package/dist/adapters/terminal/overlay.js +18 -0
- package/dist/adapters/terminal/readiness.js +11 -0
- package/dist/adapters/terminal/run.js +30 -0
- package/dist/adapters/terminal/surface.js +21 -3
- package/dist/adapters.js +32 -824
- package/dist/cli-commands.js +1 -0
- package/dist/cli.js +3 -1
- package/dist/command-contract.js +3 -0
- package/dist/commands/call.js +2 -5
- package/dist/commands/debug.js +6 -25
- package/dist/commands/device-target.js +2 -1
- package/dist/commands/doctor.js +26 -137
- package/dist/commands/fixtures.js +13 -217
- package/dist/commands/launch/device.js +2 -6
- package/dist/commands/launch/extension.js +3 -303
- package/dist/commands/launch/index.js +1 -1
- package/dist/commands/launch/mobile.js +2 -190
- package/dist/commands/launch/quote.js +7 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/parse-args.js +9 -4
- package/dist/commands/pin.js +226 -0
- package/dist/commands/prepare.js +23 -53
- package/dist/commands/provision.js +3 -7
- package/dist/commands/reload.js +8 -47
- package/dist/commands/run-engine.js +38 -138
- package/dist/commands/run-report.js +4 -48
- package/dist/commands/run.js +6 -48
- package/dist/commands/runtime-decision.js +5 -27
- package/dist/commands/self-test.js +1 -3
- package/dist/commands/status.js +20 -28
- package/dist/commands/stop.js +1 -1
- package/dist/doctor.js +12 -120
- package/dist/execution-provenance.js +3 -2
- package/dist/harness-pin.js +10 -0
- package/dist/harness.js +31 -100
- package/dist/heal-bounds.js +3 -2
- package/dist/live-adapter-contract.js +8 -34
- package/dist/manifest.js +3 -5
- package/dist/metamask-action-validation.js +5 -196
- package/dist/mm-harness-cli.js +30 -4
- package/dist/network-observation.js +10 -3
- package/dist/paths.js +8 -2
- package/dist/performance-observation.js +15 -22
- package/dist/pr-body/render.js +3 -1
- package/dist/recording-target.js +4 -39
- package/dist/run-diagnostics.js +21 -60
- package/dist/run-recording.js +5 -4
- package/dist/runner.js +20 -368
- package/dist/runtime-context.js +12 -17
- package/docs/CONTRIBUTING.md +29 -7
- package/package.json +2 -1
- package/scripts/completions.sh +2 -1
package/dist/cli-commands.js
CHANGED
|
@@ -19,6 +19,7 @@ const SPEC = {
|
|
|
19
19
|
{ name: "ports", desc: "Slot ports and runtime paths", flags: ["--json"] },
|
|
20
20
|
{ name: "up", desc: "Decide + run minimum work to reach ready", flags: ["--json", "--dry-run"] },
|
|
21
21
|
{ name: "sync", desc: "Refresh harness + canonicalize wallet fixture", flags: ["--json"] },
|
|
22
|
+
{ name: "pin", desc: "Copy this install into an immutable store for a task to run", flags: ["--json"] },
|
|
22
23
|
{ name: "logs", aliases: ["tail"], desc: "Compact build events or full log", flags: ["--full", "-f", "--window", "--events", "--source", "--json"] },
|
|
23
24
|
{ name: "debug", aliases: ["devtools", "inspect"], desc: "Open DevTools UI", flags: ["--json", "--no-open"] },
|
|
24
25
|
{ name: "reload", desc: "Reload the connected app runtime", flags: ["--json"] },
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { adapterRegistry } from "./adapters/registry.js";
|
|
1
2
|
import { detectAdapter, handleHarness } from "./harness.js";
|
|
2
3
|
import { handleActions } from "./commands/manifest.js";
|
|
3
4
|
import { handleDoctor } from "./commands/doctor.js";
|
|
@@ -108,7 +109,8 @@ async function main(argv) {
|
|
|
108
109
|
const { options } = parseArgs(argv.slice(1), command);
|
|
109
110
|
const target = targetPath(options);
|
|
110
111
|
const adapterRaw = (typeof options["adapter"] === "string" ? options["adapter"] : void 0) ?? (typeof options["platform"] === "string" ? options["platform"] : void 0) ?? detectAdapter(target);
|
|
111
|
-
|
|
112
|
+
await import("./adapters/surface.js");
|
|
113
|
+
if (adapterRaw && adapterRegistry.has(adapterRaw)) {
|
|
112
114
|
await handleHarness(["install", "--platform", adapterRaw, "--target", target, "--quiet"]);
|
|
113
115
|
}
|
|
114
116
|
} catch {
|
package/dist/command-contract.js
CHANGED
|
@@ -398,6 +398,9 @@ const PUBLIC_COMMAND_CONTRACTS = {
|
|
|
398
398
|
update: {
|
|
399
399
|
options: options(HELP, JSON, { "--check": bool() })
|
|
400
400
|
},
|
|
401
|
+
pin: {
|
|
402
|
+
options: options(HELP, JSON)
|
|
403
|
+
},
|
|
401
404
|
fixtures: {
|
|
402
405
|
options: options(HELP, JSON, TARGET, ADAPTER, ADAPTER_OR_MOBILE_PLATFORM, DEVICE, {
|
|
403
406
|
"--from": value(),
|
package/dist/commands/call.js
CHANGED
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
import { getAdapterSurface } from "../adapters/surface.js";
|
|
29
29
|
import { handleListExecutables } from "./list-executables.js";
|
|
30
30
|
import { applyDeviceTargeting } from "./device-target.js";
|
|
31
|
-
import { coreDependencyBlock } from "./core-readiness.js";
|
|
32
31
|
import {
|
|
33
32
|
emitHealViolation,
|
|
34
33
|
executeWithHealBounds,
|
|
@@ -40,7 +39,6 @@ import {
|
|
|
40
39
|
synthesizeOneNodeRecipe,
|
|
41
40
|
validateRecipeAdapterAware
|
|
42
41
|
} from "./run-engine.js";
|
|
43
|
-
import { readMobileReleaseArtifactState } from "../adapters/mobile/release-artifact-state.js";
|
|
44
42
|
import { acquireCheckoutLock } from "../checkout-lock.js";
|
|
45
43
|
import { formatRunDiagnosticsForHuman, readRunDiagnosticsDocument } from "../run-diagnostics.js";
|
|
46
44
|
import {
|
|
@@ -178,7 +176,7 @@ async function handleCall(argv) {
|
|
|
178
176
|
const resolvedAction = resolution.resolved;
|
|
179
177
|
const describedAction = describeManifestActions(manifest, actionSources).find((entry) => entry.name === resolvedAction);
|
|
180
178
|
const defaultsUsed = actionDefaultsUsed(describedAction, args);
|
|
181
|
-
const depsBlock = adapter
|
|
179
|
+
const depsBlock = await getAdapterSurface(adapter).run?.dependencyBlock?.(target, { action: resolvedAction }) ?? null;
|
|
182
180
|
if (depsBlock) {
|
|
183
181
|
if (json) {
|
|
184
182
|
console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, status: "fail", exitCode: EXIT.usage, error: depsBlock }, null, 2));
|
|
@@ -286,9 +284,8 @@ async function handleCall(argv) {
|
|
|
286
284
|
return checkoutBusyOut(json, "call", lock.message, lock.path);
|
|
287
285
|
}
|
|
288
286
|
try {
|
|
289
|
-
const authoredMobileRestart = adapter === "mobile" && resolvedAction === "app.lifecycle" && args.command === "restart";
|
|
290
287
|
const prepared = await prepareHeal(adapter, target, options, json, {
|
|
291
|
-
|
|
288
|
+
appRestartAuthored: resolvedAction === "app.lifecycle" && args.command === "restart"
|
|
292
289
|
});
|
|
293
290
|
if (typeof prepared === "number") return prepared;
|
|
294
291
|
const { state } = prepared;
|
package/dist/commands/debug.js
CHANGED
|
@@ -10,36 +10,17 @@ async function handleDebug(argv) {
|
|
|
10
10
|
const adapter = resolveAdapter(options, target);
|
|
11
11
|
if (!adapter) return usageOut(json, "debug", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
|
|
12
12
|
const surface = getAdapterSurface(adapter);
|
|
13
|
-
if (surface.headless) return usageOut(json, "debug",
|
|
13
|
+
if (surface.headless) return usageOut(json, "debug", `${adapter} is headless; there is no debug console.`, surface.hints.relaunch);
|
|
14
14
|
surface.resolveSlotPorts(target);
|
|
15
15
|
const worker = flag(options, "worker");
|
|
16
16
|
const devMenu = flag(options, "devMenu");
|
|
17
17
|
const noOpen = flag(options, "noOpen");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"--worker is extension-only (service-worker DevTools). Use --dev-menu on mobile.",
|
|
23
|
-
"mm-harness debug --dev-menu"
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
if (adapter === "extension" && devMenu) {
|
|
27
|
-
return usageOut(
|
|
28
|
-
json,
|
|
29
|
-
"debug",
|
|
30
|
-
"--dev-menu is mobile-only (RN developer menu). Use --worker on the extension.",
|
|
31
|
-
"mm-harness debug --worker"
|
|
32
|
-
);
|
|
33
|
-
}
|
|
18
|
+
const debug = surface.debug;
|
|
19
|
+
if (!debug) return usageOut(json, "debug", `${adapter} has no debug console.`, surface.hints.relaunch);
|
|
20
|
+
const refused = debug.refuse?.({ worker, devMenu });
|
|
21
|
+
if (refused) return usageOut(json, "debug", refused.message, refused.userAction);
|
|
34
22
|
const openDebug = path.join(runnerDir, "adapters/shared/open-debug.mjs");
|
|
35
|
-
const debugArgs = ["--adapter", adapter];
|
|
36
|
-
if (adapter === "extension") {
|
|
37
|
-
if (process.env.CDP_PORT) debugArgs.push("--cdp-port", process.env.CDP_PORT);
|
|
38
|
-
debugArgs.push("--target", worker ? "worker" : "page");
|
|
39
|
-
} else {
|
|
40
|
-
if (process.env.WATCHER_PORT) debugArgs.push("--port", process.env.WATCHER_PORT);
|
|
41
|
-
debugArgs.push("--action", devMenu ? "dev-menu" : "debug");
|
|
42
|
-
}
|
|
23
|
+
const debugArgs = ["--adapter", adapter, ...debug.args({ worker, devMenu })];
|
|
43
24
|
if (json) debugArgs.push("--json");
|
|
44
25
|
if (noOpen) debugArgs.push("--no-open");
|
|
45
26
|
const result = spawnScript(process.execPath, [openDebug, ...debugArgs], target, json);
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
findAvailableIosSimulatorById,
|
|
5
5
|
listConnectedDevices
|
|
6
6
|
} from "../devices.js";
|
|
7
|
+
import { getAdapterSurface } from "../adapters/surface.js";
|
|
7
8
|
import { persistExplicitDevicePin } from "../runtime-context.js";
|
|
8
9
|
import { optionString, shellQuoteArg, targetPath } from "./parse-args.js";
|
|
9
10
|
const DEVICE_SELECT_HINT = "Pass --device <name|udid|serial> to select one target. Do not shut down other connected devices to force selection.";
|
|
@@ -97,7 +98,7 @@ function deviceRecovery(options, rerun) {
|
|
|
97
98
|
function applyDeviceTargeting(command, adapter, options, opts) {
|
|
98
99
|
const device = optionString(options, "device");
|
|
99
100
|
const platformPreference = mobilePlatformPreference(options);
|
|
100
|
-
if (adapter
|
|
101
|
+
if (!getAdapterSurface(adapter).readiness.devices) {
|
|
101
102
|
if (device !== void 0) {
|
|
102
103
|
return {
|
|
103
104
|
ok: false,
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
|
-
import { readExtensionPinnedFlags } from "../adapters/extension/feature-flags.js";
|
|
6
4
|
import { featureFlagReport } from "../feature-flags.js";
|
|
7
5
|
import { color } from "../cli-color.js";
|
|
8
6
|
import { collectCheckoutView, renderCheckoutView } from "../checkout-view.js";
|
|
9
7
|
import { createDoctorReport, fixtureSummary, requiredDoctorCheckSummary } from "../doctor.js";
|
|
10
|
-
import { terminalAppPort, terminalCdpPort } from "../adapters/terminal/surface.js";
|
|
11
8
|
import { detectAdapter, resolveRuntimeContextPath } from "../harness.js";
|
|
12
9
|
import { assertAdapter, runnerDir } from "../paths.js";
|
|
13
10
|
import { getAdapterSurface } from "../adapters/surface.js";
|
|
14
|
-
import { extensionProductConfigBlock } from "../adapters/extension/product-config.js";
|
|
15
|
-
import { ensureMobilePerpsEnvironment, mobilePerpsEnvironment } from "../adapters/mobile/perps-env.js";
|
|
16
11
|
import { loadActionManifest, validateManifest } from "../manifest.js";
|
|
17
12
|
import { ensureOverlay, newHealState, recipeRunning } from "../heal-bounds.js";
|
|
18
13
|
import { applyDeviceTargeting } from "./device-target.js";
|
|
19
|
-
import {
|
|
20
|
-
mobileDeviceView,
|
|
21
|
-
mobileDeviceLiveView,
|
|
22
|
-
renderAdditionalReachableDevices,
|
|
23
|
-
renderMobileDeviceList,
|
|
24
|
-
renderMobileLiveBlock
|
|
25
|
-
} from "./mobile-device-view.js";
|
|
26
14
|
import { farmslotReadyIndicator } from "./farmslot-ready.js";
|
|
27
15
|
import { updateStatus } from "./update.js";
|
|
28
16
|
import { ensureRuntimeContext } from "../runtime-context.js";
|
|
@@ -63,6 +51,7 @@ async function handleDoctor({ options }) {
|
|
|
63
51
|
return usageOut(json, "doctor", `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
|
|
64
52
|
}
|
|
65
53
|
assertAdapter(adapter);
|
|
54
|
+
const { readiness } = getAdapterSurface(adapter);
|
|
66
55
|
if (!fs.existsSync(target)) {
|
|
67
56
|
return usageOut(
|
|
68
57
|
json,
|
|
@@ -110,10 +99,9 @@ async function handleDoctor({ options }) {
|
|
|
110
99
|
runtimeProbeError2 = error2 instanceof Error ? error2.message : String(error2);
|
|
111
100
|
}
|
|
112
101
|
const ready = runtime2?.decision === "ready";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const nextActions = doctorFixNextActions(failed, result2.fixture, adapter, target);
|
|
102
|
+
const block = readiness.runtimeBlock?.(target, runtime2);
|
|
103
|
+
if (block) failed.push(block.id);
|
|
104
|
+
const nextActions = doctorFixNextActions(failed, result2.fixture, adapter, target, block);
|
|
117
105
|
if (failed.length === 0 && !ready && runtime2?.nextAction) nextActions.push(runtime2.nextAction);
|
|
118
106
|
const status = result2.status === "pass" && failed.length === 0 ? "pass" : "fail";
|
|
119
107
|
const retryDoctor = `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`;
|
|
@@ -136,13 +124,12 @@ async function handleDoctor({ options }) {
|
|
|
136
124
|
}
|
|
137
125
|
return status === "pass" ? 0 : 1;
|
|
138
126
|
}
|
|
139
|
-
const mobilePlatform = adapter === "mobile" && (platformOption === "ios" || platformOption === "android") ? platformOption : void 0;
|
|
140
127
|
const result = createDoctorReport(
|
|
141
128
|
adapter,
|
|
142
129
|
target,
|
|
143
130
|
manifestValidation,
|
|
144
131
|
actionManifestPath,
|
|
145
|
-
|
|
132
|
+
platformOption
|
|
146
133
|
);
|
|
147
134
|
const view = collectCheckoutView(target, adapter);
|
|
148
135
|
const missingRequiredTool = result.checks.find(
|
|
@@ -183,7 +170,7 @@ async function handleDoctor({ options }) {
|
|
|
183
170
|
const checks = [
|
|
184
171
|
...result.checks,
|
|
185
172
|
runtimeCheck,
|
|
186
|
-
...
|
|
173
|
+
...await readiness.liveChecks?.(target) ?? []
|
|
187
174
|
];
|
|
188
175
|
const requiredChecks = requiredDoctorCheckSummary(checks);
|
|
189
176
|
const doctorResult = {
|
|
@@ -192,10 +179,11 @@ async function handleDoctor({ options }) {
|
|
|
192
179
|
checks,
|
|
193
180
|
requiredChecks
|
|
194
181
|
};
|
|
195
|
-
const orphanMetros =
|
|
196
|
-
const capture = captureHealth(
|
|
197
|
-
const
|
|
198
|
-
const
|
|
182
|
+
const orphanMetros = readiness.orphanDevServers?.(target, process.env.WATCHER_PORT) ?? [];
|
|
183
|
+
const capture = captureHealth(readiness.captureProviders);
|
|
184
|
+
const deviceSource = await readiness.devices?.();
|
|
185
|
+
const deviceView = deviceSource ? deviceSource.view(allDevices) : null;
|
|
186
|
+
const liveView = deviceSource && deviceView ? await deviceSource.live(target, deviceView) : null;
|
|
199
187
|
const devices = liveView?.devicesWithLive ?? deviceView?.devices ?? [];
|
|
200
188
|
const additionalReachableDevices = liveView?.additionalReachableDevices ?? [];
|
|
201
189
|
const discovery = deviceView?.deviceDiscoveryErrors.length ? { deviceDiscoveryErrors: deviceView.deviceDiscoveryErrors } : {};
|
|
@@ -216,8 +204,8 @@ async function handleDoctor({ options }) {
|
|
|
216
204
|
}
|
|
217
205
|
const skills = skillsView(target);
|
|
218
206
|
const update = updateStatus();
|
|
219
|
-
const
|
|
220
|
-
const featureFlags =
|
|
207
|
+
const pinnedFlags = readiness.pinnedFlags?.(target) ?? null;
|
|
208
|
+
const featureFlags = pinnedFlags ? featureFlagReport(pinnedFlags.overrides, pinnedFlags) : featureFlagReport(liveView?.featureFlagOverrides ?? null);
|
|
221
209
|
const doctorUserAction = doctorResult.checks.find(
|
|
222
210
|
(check) => check.required && check.status === "fail" && check.userAction
|
|
223
211
|
)?.userAction ?? `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`;
|
|
@@ -249,12 +237,7 @@ async function handleDoctor({ options }) {
|
|
|
249
237
|
featureFlags.error || (featureFlags.overrideCount ?? 0) > 0 ? "warn" : "dim",
|
|
250
238
|
featureFlags.summary.replace(/^feature flags: /u, "")
|
|
251
239
|
)}`);
|
|
252
|
-
|
|
253
|
-
const perps = mobilePerpsEnvironment(target);
|
|
254
|
-
console.log(
|
|
255
|
-
`mobile-perps: ${perps.status} (${perps.file}; MM_PERPS_ENABLED=${perps.perpsEnabled.value ?? "missing"}; OVERRIDE_REMOTE_FEATURE_FLAGS=${perps.remoteFeatureFlagOverride.value ?? "missing"})`
|
|
256
|
-
);
|
|
257
|
-
}
|
|
240
|
+
for (const line of readiness.lines?.(target) ?? []) console.log(line);
|
|
258
241
|
if (orphanMetros.length > 0) {
|
|
259
242
|
console.log(
|
|
260
243
|
`${out("warn", "orphan Metro:")} ${orphanMetros.length} leaked bundler(s) for this checkout (pids ${orphanMetros.join(", ")})`
|
|
@@ -264,33 +247,15 @@ async function handleDoctor({ options }) {
|
|
|
264
247
|
if (capture) renderCaptureHealth(capture, out);
|
|
265
248
|
renderSkillsView(skills, out);
|
|
266
249
|
renderUpdateStatus(update, out);
|
|
267
|
-
if (
|
|
268
|
-
if (deviceView)
|
|
269
|
-
if (additionalReachableDevices.length > 0)
|
|
270
|
-
if (liveView)
|
|
250
|
+
if (deviceSource) {
|
|
251
|
+
if (deviceView) deviceSource.renderList(deviceView, out);
|
|
252
|
+
if (additionalReachableDevices.length > 0) deviceSource.renderAdditional(additionalReachableDevices, out);
|
|
253
|
+
if (liveView) deviceSource.renderLive(deviceView?.devices ?? [], liveView.liveMap, out);
|
|
271
254
|
}
|
|
272
255
|
if (next) console.log(` ${out("dim", `Next: ${next}`)}`);
|
|
273
256
|
}
|
|
274
257
|
return doctorResult.status === "pass" ? 0 : 1;
|
|
275
258
|
}
|
|
276
|
-
async function terminalDoctorChecks(target) {
|
|
277
|
-
const { terminalReadiness } = await import("../../adapters/terminal/lib/readiness.mjs");
|
|
278
|
-
const { resolveBrowser } = await import("../../adapters/terminal/launch.mjs");
|
|
279
|
-
const report = await terminalReadiness({
|
|
280
|
-
target,
|
|
281
|
-
appPort: terminalAppPort(),
|
|
282
|
-
cdpPort: terminalCdpPort(),
|
|
283
|
-
signer: process.env.TERMINAL_SIGNER || "extension",
|
|
284
|
-
account: process.env.TERMINAL_ACCOUNT || "dev1",
|
|
285
|
-
probeBrowser: resolveBrowser
|
|
286
|
-
});
|
|
287
|
-
return report.checks.map((check) => ({
|
|
288
|
-
id: `terminal-${check.id}`,
|
|
289
|
-
status: check.status === "pass" ? "pass" : "fail",
|
|
290
|
-
required: check.required,
|
|
291
|
-
message: check.detail
|
|
292
|
-
}));
|
|
293
|
-
}
|
|
294
259
|
function doctorRuntimeCheck(runtime, error, expectLive) {
|
|
295
260
|
if (error) {
|
|
296
261
|
return {
|
|
@@ -310,7 +275,7 @@ function doctorRuntimeCheck(runtime, error, expectLive) {
|
|
|
310
275
|
detail: runtime?.reasons.join(" | ") || "Runtime readiness probe returned no result."
|
|
311
276
|
};
|
|
312
277
|
}
|
|
313
|
-
function doctorFixNextActions(failed, fixture, adapter, target) {
|
|
278
|
+
function doctorFixNextActions(failed, fixture, adapter, target, block) {
|
|
314
279
|
const actions = [];
|
|
315
280
|
const executable = process.env.MM_HARNESS_EXECUTABLE ? shellQuote(process.env.MM_HARNESS_EXECUTABLE) : "mm-harness";
|
|
316
281
|
const scope = `--adapter ${adapter} --target ${shellQuote(target)}`;
|
|
@@ -325,10 +290,7 @@ function doctorFixNextActions(failed, fixture, adapter, target) {
|
|
|
325
290
|
);
|
|
326
291
|
}
|
|
327
292
|
if (failed.includes("overlay")) actions.push(`${executable} install ${scope}`);
|
|
328
|
-
if (
|
|
329
|
-
const block = extensionProductConfigBlock(target);
|
|
330
|
-
if (block) actions.push(block.userAction);
|
|
331
|
-
}
|
|
293
|
+
if (block?.userAction && failed.includes(block.id)) actions.push(block.userAction);
|
|
332
294
|
return actions;
|
|
333
295
|
}
|
|
334
296
|
function emitExpectLiveVerdict(adapter, target, runtime, verdict) {
|
|
@@ -367,9 +329,9 @@ function emitExpectLive(adapter, target, runtime, result, orphanMetros, capture,
|
|
|
367
329
|
emitExpectLiveVerdict(adapter, target, runtime, "not-live");
|
|
368
330
|
return EXIT.runtime;
|
|
369
331
|
}
|
|
370
|
-
function captureHealth(
|
|
371
|
-
if (
|
|
372
|
-
const fallback =
|
|
332
|
+
function captureHealth(providers) {
|
|
333
|
+
if (!providers) return null;
|
|
334
|
+
const fallback = [...providers];
|
|
373
335
|
const helper = captureHelperHealth();
|
|
374
336
|
if (!helper) {
|
|
375
337
|
return {
|
|
@@ -445,18 +407,6 @@ function renderUpdateStatus(status, out) {
|
|
|
445
407
|
status.updateAvailable ? `${out("label", "update:")} ${out("warn", `${status.current} \u2192 ${status.latest} available`)} ${out("dim", "(mm-harness update)")}` : status.latest && status.latest !== status.current ? `${out("label", "update:")} ${out("ok", status.current)} ${out("dim", `ahead of registry ${status.latest}`)}` : `${out("label", "update:")} ${out("ok", `${status.current} is latest`)}`
|
|
446
408
|
);
|
|
447
409
|
}
|
|
448
|
-
function detectOrphanMetros(target, livePort) {
|
|
449
|
-
try {
|
|
450
|
-
const script = path.join(runnerDir, "adapters/shared/reap-checkout-metros.sh");
|
|
451
|
-
const out = execFileSync("bash", ["-c", `. "$1"; detect_checkout_metros "$2" "$3"`, "mm-harness-doctor", script, target, livePort ?? ""], {
|
|
452
|
-
encoding: "utf8",
|
|
453
|
-
timeout: 5e3
|
|
454
|
-
});
|
|
455
|
-
return out.split("\n").map((line) => line.trim()).filter((line) => /^\d+$/u.test(line));
|
|
456
|
-
} catch {
|
|
457
|
-
return [];
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
410
|
async function runDoctorFix(adapter, target, manifestValidation, json) {
|
|
461
411
|
const fixed = [];
|
|
462
412
|
const failed = [];
|
|
@@ -473,18 +423,11 @@ async function runDoctorFix(adapter, target, manifestValidation, json) {
|
|
|
473
423
|
} catch {
|
|
474
424
|
failed.push("runtime-context");
|
|
475
425
|
}
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
if (ensureExtensionPerpsBuildFlag(target)) fixed.push("extension-perps-build");
|
|
479
|
-
} catch {
|
|
480
|
-
failed.push("extension-perps-build");
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (adapter === "mobile") {
|
|
426
|
+
for (const fix of getAdapterSurface(adapter).readiness.fixes ?? []) {
|
|
484
427
|
try {
|
|
485
|
-
if (
|
|
428
|
+
if (fix.apply(target)) fixed.push(fix.id);
|
|
486
429
|
} catch {
|
|
487
|
-
failed.push(
|
|
430
|
+
failed.push(fix.id);
|
|
488
431
|
}
|
|
489
432
|
}
|
|
490
433
|
const state = newHealState();
|
|
@@ -508,62 +451,8 @@ async function runDoctorFix(adapter, target, manifestValidation, json) {
|
|
|
508
451
|
}
|
|
509
452
|
return { fixed, failed };
|
|
510
453
|
}
|
|
511
|
-
function ensureExtensionPerpsBuildFlag(target) {
|
|
512
|
-
const configPath = path.join(target, ".metamaskrc");
|
|
513
|
-
const templatePath = path.join(target, ".metamaskrc.dist");
|
|
514
|
-
const config = readTextSnapshot(configPath);
|
|
515
|
-
const template = config.kind === "file" ? null : readTextSnapshot(templatePath);
|
|
516
|
-
const source = config.kind === "file" ? config : template?.kind === "file" ? template : config.kind === "symlink" ? { kind: "file", text: "", mode: 438 } : null;
|
|
517
|
-
if (!source) return false;
|
|
518
|
-
const before = source.text;
|
|
519
|
-
if (/^\s*PERPS_ENABLED\s*=\s*(['"]?)true\1\s*$/mu.test(before)) return false;
|
|
520
|
-
const line = "PERPS_ENABLED='true'";
|
|
521
|
-
const after = /^\s*PERPS_ENABLED\s*=.*$/mu.test(before) ? before.replace(/^\s*PERPS_ENABLED\s*=.*$/mu, line) : `${before.replace(/\s*$/u, "")}
|
|
522
|
-
${line}
|
|
523
|
-
`;
|
|
524
|
-
const temporary = `${configPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
525
|
-
let descriptor = null;
|
|
526
|
-
try {
|
|
527
|
-
const mode = config.kind === "file" ? config.mode : 438;
|
|
528
|
-
descriptor = fs.openSync(
|
|
529
|
-
temporary,
|
|
530
|
-
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL,
|
|
531
|
-
mode
|
|
532
|
-
);
|
|
533
|
-
fs.writeFileSync(descriptor, after);
|
|
534
|
-
if (config.kind === "file") fs.fchmodSync(descriptor, config.mode);
|
|
535
|
-
fs.closeSync(descriptor);
|
|
536
|
-
descriptor = null;
|
|
537
|
-
fs.renameSync(temporary, configPath);
|
|
538
|
-
} finally {
|
|
539
|
-
if (descriptor !== null) fs.closeSync(descriptor);
|
|
540
|
-
fs.rmSync(temporary, { force: true });
|
|
541
|
-
}
|
|
542
|
-
return true;
|
|
543
|
-
}
|
|
544
|
-
function readTextSnapshot(file) {
|
|
545
|
-
let descriptor;
|
|
546
|
-
try {
|
|
547
|
-
descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
548
|
-
} catch (error) {
|
|
549
|
-
const code = error.code;
|
|
550
|
-
if (code === "ENOENT") return { kind: "missing" };
|
|
551
|
-
if (code === "ELOOP") return { kind: "symlink" };
|
|
552
|
-
throw error;
|
|
553
|
-
}
|
|
554
|
-
try {
|
|
555
|
-
return {
|
|
556
|
-
kind: "file",
|
|
557
|
-
text: fs.readFileSync(descriptor, "utf8"),
|
|
558
|
-
mode: fs.fstatSync(descriptor).mode & 511
|
|
559
|
-
};
|
|
560
|
-
} finally {
|
|
561
|
-
fs.closeSync(descriptor);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
454
|
export {
|
|
565
455
|
doctorFixNextActions,
|
|
566
|
-
ensureExtensionPerpsBuildFlag,
|
|
567
456
|
handleDoctor,
|
|
568
457
|
skillsView
|
|
569
458
|
};
|