@deeeed/metamask-harness 0.17.4 → 0.17.5
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 +6 -103
- package/README.md +136 -96
- package/adapters/extension/console-tail.mjs +24 -55
- package/adapters/extension/ensure-browser.sh +7 -1
- package/adapters/extension/inject.mjs +15 -5
- package/adapters/extension/launch-browser.cjs +4 -3
- package/adapters/extension/launch.sh +11 -8
- package/adapters/extension/lib/chrome-args.cjs +24 -0
- package/adapters/extension/live.sh +1 -2
- package/adapters/extension/reattach.sh +0 -1
- package/adapters/extension/seed-fixture.sh +12 -4
- package/adapters/extension/sidepanel-toggle.sh +1 -4
- package/adapters/extension/start-watch.sh +4 -17
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/verify.sh +1 -1
- package/adapters/manifest.json +45 -53
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -16
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +1 -3
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +3 -2
- package/adapters/mobile/cleanup.sh +176 -16
- package/adapters/mobile/inject.sh +671 -90
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/adapters/mobile/overlay/compat/README.md +28 -0
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +42 -0
- package/adapters/mobile/start-metro.sh +41 -0
- package/adapters/mobile/verify.sh +61 -4
- package/adapters/mobile/wait-for-bridge.sh +8 -22
- package/adapters/shared/harness-source-fingerprint.mjs +49 -0
- package/adapters/shared/install-repo-deps.sh +1 -5
- package/adapters/shared/open-debug.mjs +184 -103
- package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +5 -0
- package/adapters/shared/resolve-farmslot-ports.sh +7 -0
- package/adapters/shared/resolve-slot-ports-core.mjs +6 -0
- package/adapters/shared/resolve-slot-ports.sh +7 -2
- package/bin/mm-harness +0 -7
- package/dist/adapters/extension/harness-freshness.js +39 -0
- package/dist/adapters/extension/runtime-decision.js +4 -73
- package/dist/adapters/extension/surface.js +2 -3
- package/dist/adapters/mobile/perps-env.js +3 -43
- package/dist/adapters/mobile/prepare.js +7 -22
- package/dist/adapters/mobile/source-freshness.js +26 -41
- package/dist/adapters/mobile/surface.js +1 -1
- package/dist/adapters/resolve-slot-ports.js +2 -0
- package/dist/adapters/slot-ports.js +0 -16
- package/dist/adapters.js +5 -12
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +12 -6
- package/dist/commands/call.js +22 -56
- package/dist/commands/check.js +3 -9
- package/dist/commands/checklist.js +4 -1
- package/dist/commands/debug.js +31 -38
- package/dist/commands/device-target.js +12 -27
- package/dist/commands/doctor.js +23 -77
- package/dist/commands/fixtures.js +17 -27
- package/dist/commands/launch/extension.js +2 -0
- package/dist/commands/launch/index.js +59 -167
- package/dist/commands/logs.js +6 -8
- package/dist/commands/manifest.js +25 -151
- package/dist/commands/parse-args.js +0 -2
- package/dist/commands/provision.js +3 -10
- package/dist/commands/run-engine.js +69 -71
- package/dist/commands/run-report.js +3 -12
- package/dist/commands/run.js +35 -245
- package/dist/commands/shared.js +3 -55
- package/dist/commands/status-probe.js +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/stop.js +2 -7
- package/dist/commands/update.js +28 -16
- package/dist/doctor.js +4 -1
- package/dist/harness.js +134 -43
- package/dist/heal-bounds.js +1 -1
- package/dist/live-adapter-contract.js +20 -6
- package/dist/mm-harness-cli.js +48 -123
- package/dist/paths.js +5 -2
- package/dist/runner.js +2 -37
- package/dist/runtime-context.js +8 -26
- package/docs/ADAPTER-SURFACE.md +112 -19
- package/docs/CHEATSHEET.md +52 -39
- package/docs/CLI-SPEC.md +1098 -0
- package/docs/CODE-MAP.md +1 -1
- package/docs/DEBUG-HANDOVER.md +36 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/UX-PRINCIPLES.md +1 -3
- package/docs/architecture.md +375 -65
- package/docs/live-adapter-contract.md +2 -4
- package/docs/recipe-libraries.md +275 -45
- package/library/README.md +0 -4
- package/library/actions/core/perps/_controller.mjs +55 -10
- package/library/actions/mobile/platform/bridge.mjs +1 -5
- package/library/manifests/core.action-manifest.json +7 -7
- package/library/manifests/mobile.action-manifest.json +3 -28
- package/package.json +1 -4
- package/adapters/extension/launch-webpack.cjs +0 -83
- package/adapters/extension/stamp-runtime-title.cjs +0 -58
- package/adapters/extension/sync-webpack-dist.cjs +0 -107
- package/adapters/mobile/launch-console-forwarder.cjs +0 -39
- package/adapters/mobile/start-console-forwarder.sh +0 -70
- package/adapters/shared/update-check-worker.mjs +0 -43
- package/dist/adapters/extension/console-capture.js +0 -93
- package/dist/command-contract.js +0 -441
- package/dist/command-journal.js +0 -225
- package/dist/commands/last.js +0 -52
- package/dist/json-stream.js +0 -57
- package/dist/run-diagnostics.js +0 -261
- package/docs/CLI-ERGONOMICS-AUDIT.md +0 -32
- package/docs/CLI-ERGONOMICS-HUMAN-QA.md +0 -64
- package/docs/VIDEO-DEMO-VALIDATION.md +0 -74
- package/library/recipes/runner/smoke.core.recipe.json +0 -27
- package/library/recipes/wallet/smoke.extension.recipe.json +0 -42
- package/library/recipes/wallet/smoke.mobile.recipe.json +0 -42
- package/scripts/validate-human-outcomes.mjs +0 -169
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
const DEFAULTS = {
|
|
@@ -25,53 +24,14 @@ function ensureMobilePerpsEnvironment(target) {
|
|
|
25
24
|
(key) => settingForKey(current, key).status === "missing"
|
|
26
25
|
);
|
|
27
26
|
if (missing.length === 0) return [];
|
|
28
|
-
const file = path.join(
|
|
29
|
-
const
|
|
30
|
-
const before = snapshot?.text ?? "";
|
|
27
|
+
const file = path.join(target, current.file);
|
|
28
|
+
const before = current.fileExists ? fs.readFileSync(file, "utf8") : "";
|
|
31
29
|
const separator = before.length > 0 && !before.endsWith("\n") ? "\n" : "";
|
|
32
30
|
const additions = missing.map((key) => `export ${key}="${DEFAULTS[key]}"`).join("\n");
|
|
33
|
-
|
|
34
|
-
let descriptor = null;
|
|
35
|
-
try {
|
|
36
|
-
descriptor = fs.openSync(
|
|
37
|
-
temporary,
|
|
38
|
-
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL,
|
|
39
|
-
snapshot?.mode ?? 384
|
|
40
|
-
);
|
|
41
|
-
fs.writeFileSync(descriptor, `${before}${separator}${additions}
|
|
31
|
+
fs.writeFileSync(file, `${before}${separator}${additions}
|
|
42
32
|
`);
|
|
43
|
-
if (snapshot) fs.fchmodSync(descriptor, snapshot.mode);
|
|
44
|
-
fs.closeSync(descriptor);
|
|
45
|
-
descriptor = null;
|
|
46
|
-
fs.renameSync(temporary, file);
|
|
47
|
-
} finally {
|
|
48
|
-
if (descriptor !== null) fs.closeSync(descriptor);
|
|
49
|
-
fs.rmSync(temporary, { force: true });
|
|
50
|
-
}
|
|
51
33
|
return missing;
|
|
52
34
|
}
|
|
53
|
-
function readEnvironmentSnapshot(file) {
|
|
54
|
-
let descriptor;
|
|
55
|
-
try {
|
|
56
|
-
descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
const code = error.code;
|
|
59
|
-
if (code === "ENOENT") return null;
|
|
60
|
-
if (code === "ELOOP") {
|
|
61
|
-
throw new Error(`.js.env must not be a symbolic link. Next: replace ${file} with a regular file.`);
|
|
62
|
-
}
|
|
63
|
-
throw error;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const stat = fs.fstatSync(descriptor);
|
|
67
|
-
if (!stat.isFile()) {
|
|
68
|
-
throw new Error(`.js.env must be a regular file. Next: replace ${file} with a regular file.`);
|
|
69
|
-
}
|
|
70
|
-
return { text: fs.readFileSync(descriptor, "utf8"), mode: stat.mode & 511 };
|
|
71
|
-
} finally {
|
|
72
|
-
fs.closeSync(descriptor);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
35
|
function readSetting(source, key) {
|
|
76
36
|
const assignment = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=(.*)$`, "u");
|
|
77
37
|
let value = null;
|
|
@@ -56,21 +56,14 @@ async function prepareMobile(target, opts = {}) {
|
|
|
56
56
|
json,
|
|
57
57
|
preflightMode
|
|
58
58
|
);
|
|
59
|
-
if (confirm.status === 0) return
|
|
59
|
+
if (confirm.status === 0) return { status: 0, output: "" };
|
|
60
60
|
if (!json) {
|
|
61
61
|
process.stderr.write(
|
|
62
62
|
`launch: runtime looked ready but no ${platform} bridge target answered; launching the app.
|
|
63
63
|
`
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
launchActions(path.resolve(target)),
|
|
68
|
-
target,
|
|
69
|
-
platform,
|
|
70
|
-
json,
|
|
71
|
-
preflightMode
|
|
72
|
-
);
|
|
73
|
-
return launch.status === 0 ? startMobileConsoleForwarder(target, platform, json) : launch;
|
|
66
|
+
return dispatchActionSequence(launchActions(path.resolve(target)), target, platform, json, preflightMode);
|
|
74
67
|
}
|
|
75
68
|
if (report.decision === "unknown") {
|
|
76
69
|
const msg = "mobile prepare: runtime state unknown\n Next: run mm-harness verify --adapter mobile --target <checkout>";
|
|
@@ -118,15 +111,7 @@ async function prepareMobile(target, opts = {}) {
|
|
|
118
111
|
}
|
|
119
112
|
}
|
|
120
113
|
}
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
function startMobileConsoleForwarder(target, platform, json) {
|
|
124
|
-
return dispatchAction(
|
|
125
|
-
{ id: "start-console-forwarder", cwd: target, argv: json ? ["--quiet"] : [] },
|
|
126
|
-
target,
|
|
127
|
-
platform,
|
|
128
|
-
json
|
|
129
|
-
);
|
|
114
|
+
return { status: 0, output: "" };
|
|
130
115
|
}
|
|
131
116
|
async function dispatchActionSequence(requestedActions, target, platform, json, preflightMode) {
|
|
132
117
|
const resolved = path.resolve(target);
|
|
@@ -162,10 +147,6 @@ async function dispatchAction(action, target, platform, json, preflightMode = "f
|
|
|
162
147
|
const extra = action.argv ?? [];
|
|
163
148
|
return spawnScriptStreaming(leaf, ["--target", cwd, ...extra], target);
|
|
164
149
|
}
|
|
165
|
-
case "start-console-forwarder": {
|
|
166
|
-
const leaf = path.join(runnerDir, "adapters/mobile/start-console-forwarder.sh");
|
|
167
|
-
return spawnScriptStreaming(leaf, ["--target", cwd, ...action.argv ?? []], target);
|
|
168
|
-
}
|
|
169
150
|
case "prewarm-bundle": {
|
|
170
151
|
const leaf = path.join(runnerDir, "adapters/mobile/prewarm-bundle.sh");
|
|
171
152
|
return spawnScriptStreaming(leaf, ["--platform", platform, "--target", cwd], target);
|
|
@@ -175,6 +156,10 @@ async function dispatchAction(action, target, platform, json, preflightMode = "f
|
|
|
175
156
|
const extra = action.argv ?? [];
|
|
176
157
|
return spawnScriptStreaming(leaf, ["--target", cwd, "--platform", platform, ...extra], target);
|
|
177
158
|
}
|
|
159
|
+
case "clear-metro-cache": {
|
|
160
|
+
const leaf = path.join(runnerDir, "adapters/mobile/start-metro.sh");
|
|
161
|
+
return spawnScriptStreaming(leaf, ["--target", cwd, "--clear"], target);
|
|
162
|
+
}
|
|
178
163
|
case "launch-mobile-runtime": {
|
|
179
164
|
const leaf = path.join(runnerDir, "adapters/mobile/open-device.sh");
|
|
180
165
|
return spawnScriptStreaming(
|
|
@@ -20,47 +20,32 @@ const SOURCE_PATHS = [
|
|
|
20
20
|
const ENV_INPUTS = [".js.env", ".env", ".env.local"];
|
|
21
21
|
const BASELINE_FILE = "mobile-source-baseline.json";
|
|
22
22
|
function mobileSourceFingerprint(target) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
hash.update("\0");
|
|
45
|
-
}
|
|
46
|
-
return { status: "ok", fingerprint: hash.digest("hex") };
|
|
47
|
-
} catch {
|
|
48
|
-
const resolved = path.resolve(target);
|
|
49
|
-
return {
|
|
50
|
-
fingerprint: null,
|
|
51
|
-
status: "unavailable",
|
|
52
|
-
error: {
|
|
53
|
-
code: "MOBILE_SOURCE_UNAVAILABLE",
|
|
54
|
-
message: `Mobile source fingerprint requires a readable Git checkout and source inputs: ${resolved}`,
|
|
55
|
-
userAction: "mm-harness doctor --adapter mobile --target <metamask-mobile-checkout>"
|
|
56
|
-
}
|
|
57
|
-
};
|
|
23
|
+
const hash = createHash("sha256");
|
|
24
|
+
hash.update(git(target, ["rev-parse", "HEAD"]));
|
|
25
|
+
hash.update(git(target, ["diff", "--no-ext-diff", "--binary", "HEAD", "--", ...SOURCE_PATHS]));
|
|
26
|
+
const untracked = git(target, [
|
|
27
|
+
"ls-files",
|
|
28
|
+
"--others",
|
|
29
|
+
"--exclude-standard",
|
|
30
|
+
"-z",
|
|
31
|
+
"--",
|
|
32
|
+
...SOURCE_PATHS
|
|
33
|
+
]).toString("utf8").split("\0").filter(Boolean).sort();
|
|
34
|
+
for (const relative of untracked) {
|
|
35
|
+
hash.update(`untracked\0${relative}\0`);
|
|
36
|
+
hash.update(fs.readFileSync(path.join(target, relative)));
|
|
37
|
+
hash.update("\0");
|
|
38
|
+
}
|
|
39
|
+
for (const relative of ENV_INPUTS) {
|
|
40
|
+
const absolute = path.join(target, relative);
|
|
41
|
+
hash.update(`env\0${relative}\0`);
|
|
42
|
+
hash.update(fs.existsSync(absolute) ? fs.readFileSync(absolute) : "<absent>");
|
|
43
|
+
hash.update("\0");
|
|
58
44
|
}
|
|
45
|
+
return hash.digest("hex");
|
|
59
46
|
}
|
|
60
47
|
function mobileSourceCheck(target) {
|
|
61
|
-
const
|
|
62
|
-
if (result.status === "unavailable") return result;
|
|
63
|
-
const { fingerprint } = result;
|
|
48
|
+
const fingerprint = mobileSourceFingerprint(target);
|
|
64
49
|
const baseline = readBaseline(target);
|
|
65
50
|
return {
|
|
66
51
|
fingerprint,
|
|
@@ -68,7 +53,7 @@ function mobileSourceCheck(target) {
|
|
|
68
53
|
};
|
|
69
54
|
}
|
|
70
55
|
function recordMobileSourceBaseline(target, expectedFingerprint) {
|
|
71
|
-
if (mobileSourceFingerprint(target)
|
|
56
|
+
if (mobileSourceFingerprint(target) !== expectedFingerprint) return false;
|
|
72
57
|
const marker = recipeRuntimePath(target, BASELINE_FILE);
|
|
73
58
|
const temporary = `${marker}.${process.pid}.${Date.now()}.tmp`;
|
|
74
59
|
const baseline = {
|
|
@@ -80,9 +65,9 @@ function recordMobileSourceBaseline(target, expectedFingerprint) {
|
|
|
80
65
|
try {
|
|
81
66
|
fs.writeFileSync(temporary, `${JSON.stringify(baseline, null, 2)}
|
|
82
67
|
`, { mode: 384 });
|
|
83
|
-
if (mobileSourceFingerprint(target)
|
|
68
|
+
if (mobileSourceFingerprint(target) !== expectedFingerprint) return false;
|
|
84
69
|
fs.renameSync(temporary, marker);
|
|
85
|
-
if (mobileSourceFingerprint(target)
|
|
70
|
+
if (mobileSourceFingerprint(target) !== expectedFingerprint) {
|
|
86
71
|
if (readBaseline(target)?.fingerprint === expectedFingerprint) fs.rmSync(marker, { force: true });
|
|
87
72
|
return false;
|
|
88
73
|
}
|
|
@@ -47,7 +47,7 @@ const mobileSurface = {
|
|
|
47
47
|
return [{ label: "metro", path: recipeRuntimePath(target, "metro.log") }];
|
|
48
48
|
},
|
|
49
49
|
appLogSource(target) {
|
|
50
|
-
return { label: "app", path: recipeRuntimePath(target, "
|
|
50
|
+
return { label: "app", path: recipeRuntimePath(target, "metro.log") };
|
|
51
51
|
},
|
|
52
52
|
hints: {
|
|
53
53
|
launch: "mm-harness launch ios",
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
resolveExtensionRuntimeContext,
|
|
8
8
|
resolveExtensionRuntimePorts,
|
|
9
9
|
resolveSlotPortsByRepo,
|
|
10
|
+
resolveFarmslotPortsByRepo,
|
|
10
11
|
resolveMobileRuntimeContext,
|
|
11
12
|
resolveMobileRuntimePorts,
|
|
12
13
|
resolveMobileSlotDefaults
|
|
@@ -19,6 +20,7 @@ export {
|
|
|
19
20
|
resolveDefaultExtensionPorts,
|
|
20
21
|
resolveExtensionRuntimeContext,
|
|
21
22
|
resolveExtensionRuntimePorts,
|
|
23
|
+
resolveFarmslotPortsByRepo,
|
|
22
24
|
resolveMobileRuntimeContext,
|
|
23
25
|
resolveMobileRuntimePorts,
|
|
24
26
|
resolveMobileSlotDefaults,
|
|
@@ -105,32 +105,17 @@ function isExtensionWatcherLive(target) {
|
|
|
105
105
|
function stopExtensionWatcher(target) {
|
|
106
106
|
const runtimeAbs = path.join(path.resolve(target), recipeRuntimeDir());
|
|
107
107
|
const webpackPidFile = path.join(runtimeAbs, "recipe-harness-webpack.pid");
|
|
108
|
-
const syncPidFile = path.join(runtimeAbs, "recipe-harness-webpack-sync.pid");
|
|
109
108
|
const ownedPids = extensionWatcherPids(target);
|
|
110
|
-
try {
|
|
111
|
-
const syncPid = fs.readFileSync(syncPidFile, "utf8").trim();
|
|
112
|
-
if (/^\d+$/u.test(syncPid) && processAlive(Number(syncPid))) ownedPids.push(Number(syncPid));
|
|
113
|
-
} catch {
|
|
114
|
-
}
|
|
115
109
|
for (const pid of ownedPids) {
|
|
116
|
-
try {
|
|
117
|
-
process.kill(-pid, "SIGTERM");
|
|
118
|
-
} catch {
|
|
119
|
-
}
|
|
120
110
|
try {
|
|
121
111
|
process.kill(pid, "SIGTERM");
|
|
122
112
|
} catch {
|
|
123
113
|
}
|
|
124
114
|
}
|
|
125
115
|
fs.rmSync(webpackPidFile, { force: true });
|
|
126
|
-
fs.rmSync(syncPidFile, { force: true });
|
|
127
116
|
if (ownedPids.length > 0) {
|
|
128
117
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2e3);
|
|
129
118
|
for (const pid of ownedPids) {
|
|
130
|
-
try {
|
|
131
|
-
process.kill(-pid, "SIGKILL");
|
|
132
|
-
} catch {
|
|
133
|
-
}
|
|
134
119
|
try {
|
|
135
120
|
process.kill(pid, "SIGKILL");
|
|
136
121
|
} catch {
|
|
@@ -179,7 +164,6 @@ function stopExtensionRuntime(target) {
|
|
|
179
164
|
let removedMarkers = 0;
|
|
180
165
|
for (const name of [
|
|
181
166
|
"recipe-harness-webpack.pid",
|
|
182
|
-
"recipe-harness-webpack-sync.pid",
|
|
183
167
|
"webpack.pid",
|
|
184
168
|
"browser.pid",
|
|
185
169
|
"chromium.pid",
|
package/dist/adapters.js
CHANGED
|
@@ -287,15 +287,8 @@ async function handleMobileNavigate(payload, context) {
|
|
|
287
287
|
return isRecord(live.result) ? { ...live.result, liveAdapter: live.script } : live.result;
|
|
288
288
|
}
|
|
289
289
|
async function handleMobilePress(payload, context) {
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
if (identifiers.length + Number(text !== void 0) !== 1) {
|
|
293
|
-
throw new Error("ui.press requires exactly one of: test_id, testID, selector, text.");
|
|
294
|
-
}
|
|
295
|
-
const target = text ?? identifiers[0];
|
|
296
|
-
if (target.trim() === "") throw new Error("ui.press target must be non-empty.");
|
|
297
|
-
const input = mobileUiInput(context, "press", payload);
|
|
298
|
-
return text !== void 0 ? bridgeCommand(input, ["press-text", target]) : bridgeCommand(input, ["press-test-id", target]);
|
|
290
|
+
const target = firstScalarText(payload, ["test_id", "testID", "selector", "text"], "ui.press");
|
|
291
|
+
return bridgeCommand(mobileUiInput(context, "press", payload), ["press-test-id", target]);
|
|
299
292
|
}
|
|
300
293
|
async function handleMobileSetInput(payload, context) {
|
|
301
294
|
const input = mobileUiInput(context, "set_input", payload);
|
|
@@ -472,12 +465,12 @@ function createMetaMaskUiTransport(platform, harness) {
|
|
|
472
465
|
if (live) return live.output;
|
|
473
466
|
throw new Error(`ui.navigate requires library/actions/${platform}/ui/navigate.mjs.`);
|
|
474
467
|
}
|
|
475
|
-
return base.execute(action, action === "ui.wait_for" ? normalizeUiWaitNode(node
|
|
468
|
+
return base.execute(action, action === "ui.wait_for" ? normalizeUiWaitNode(node) : node, context);
|
|
476
469
|
}
|
|
477
470
|
};
|
|
478
471
|
}
|
|
479
|
-
function normalizeUiWaitNode(node
|
|
480
|
-
if (
|
|
472
|
+
function normalizeUiWaitNode(node) {
|
|
473
|
+
if (node.expected !== void 0 || typeof node.visible !== "boolean") return node;
|
|
481
474
|
return { ...node, expected: node.visible ? "visible" : "absent" };
|
|
482
475
|
}
|
|
483
476
|
function createMetaMaskAdapters(adapter) {
|
package/dist/cli-commands.js
CHANGED
|
@@ -15,7 +15,7 @@ const SPEC = {
|
|
|
15
15
|
{ name: "logs", aliases: ["tail"], desc: "Compact build events or full log", flags: ["--full", "-f", "--window", "--events", "--source", "--json"] },
|
|
16
16
|
{ name: "debug", aliases: ["devtools", "inspect"], desc: "Open DevTools UI", flags: ["--json", "--no-open"] },
|
|
17
17
|
{ name: "fixtures", desc: "Manage the canonical wallet fixture (sync/set/generate)", args: ["sync", "set", "generate"], flags: ["--fixture", "--out", "--adapter", "--target", "--device", "--json"] },
|
|
18
|
-
{ name: "actions", desc: "List runnable recipe actions", flags: ["--json"
|
|
18
|
+
{ name: "actions", desc: "List runnable recipe actions", flags: ["--json"] },
|
|
19
19
|
{ name: "doctor", desc: "Check harness/orchestration health", flags: ["--json", "--target", "--adapter", "--runtime-dir", "--expect-live", "--print-ready", "--cdp-port", "--device"] },
|
|
20
20
|
{ name: "run", desc: "Execute a proof recipe (path or library name, e.g. run perps.smoke)", args: ["recipe.json|name"], flags: ["--list", "--device"] },
|
|
21
21
|
{ name: "recipe-quality", desc: "Build the recipe-quality artifact from compact JSON", args: ["build"], flags: ["--input", "--output", "--json"] },
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { detectAdapter, handleHarness } from "./harness.js";
|
|
2
|
-
import { handleActions } from "./commands/manifest.js";
|
|
2
|
+
import { handleActions, handleManifest } from "./commands/manifest.js";
|
|
3
3
|
import { handleDoctor } from "./commands/doctor.js";
|
|
4
4
|
import { handleProvision } from "./commands/provision.js";
|
|
5
5
|
import { handleRuntimeHealth } from "./commands/runtime-health.js";
|
|
@@ -21,10 +21,10 @@ import { handleRecipeQuality } from "./commands/recipe-quality.js";
|
|
|
21
21
|
import { handleStatus } from "./commands/status.js";
|
|
22
22
|
import { handleCheck } from "./commands/check.js";
|
|
23
23
|
import { handleChecklist } from "./commands/checklist.js";
|
|
24
|
-
import { handleLast } from "./commands/last.js";
|
|
25
24
|
import { parseArgs, targetPath } from "./commands/parse-args.js";
|
|
26
25
|
import { runOneNode } from "./commands/run-engine.js";
|
|
27
26
|
const COMMANDS = {
|
|
27
|
+
manifest: handleManifest,
|
|
28
28
|
actions: handleActions,
|
|
29
29
|
doctor: handleDoctor,
|
|
30
30
|
"runtime-health": handleRuntimeHealth,
|
|
@@ -35,7 +35,7 @@ const COMMANDS = {
|
|
|
35
35
|
run: handleRun,
|
|
36
36
|
"self-test": handleSelfTest
|
|
37
37
|
};
|
|
38
|
-
const OVERLAY_COMMANDS = ["install", "verify", "cleanup"];
|
|
38
|
+
const OVERLAY_COMMANDS = ["install", "verify", "cleanup", "live"];
|
|
39
39
|
function usage() {
|
|
40
40
|
console.error(`mm-harness \u2014 the MetaMask recipe harness: launch the app, prove behavior, manage the runtime overlay.
|
|
41
41
|
Run it from inside a MetaMask checkout; the platform (mobile | extension | core) is auto-detected.
|
|
@@ -62,6 +62,9 @@ PROVE \u2014 run recipes and inspect capabilities:
|
|
|
62
62
|
mm-harness check diff --profile fast --artifacts-dir artifacts/validation
|
|
63
63
|
actions Describe the actions a manifest declares.
|
|
64
64
|
mm-harness actions --adapter mobile
|
|
65
|
+
manifest Print/validate the action manifest for an adapter.
|
|
66
|
+
mm-harness manifest --adapter extension --json
|
|
67
|
+
|
|
65
68
|
RUNTIME OVERLAY \u2014 install/verify/clean the per-checkout runtime overlay:
|
|
66
69
|
provision Install the cached Runway mobile dev client (artifact cache + sim install).
|
|
67
70
|
mm-harness provision runway ios --adapter mobile
|
|
@@ -69,8 +72,12 @@ RUNTIME OVERLAY \u2014 install/verify/clean the per-checkout runtime overlay:
|
|
|
69
72
|
mm-harness install # inside a checkout, auto-detected
|
|
70
73
|
verify Check the overlay/runtime is present and healthy (no launch).
|
|
71
74
|
mm-harness verify
|
|
75
|
+
live Launch/reuse the app, then verify live control end-to-end.
|
|
76
|
+
mm-harness live
|
|
72
77
|
cleanup Remove the installed overlay and restore the checkout.
|
|
73
78
|
mm-harness cleanup
|
|
79
|
+
# Open sub-question (Arthur): keep these top-level (mm-harness install) or group
|
|
80
|
+
# them under an \`overlay\` command (mm-harness overlay install)? Top-level for now.
|
|
74
81
|
|
|
75
82
|
ADVANCED \u2014 internal runtime probes (rarely typed by hand):
|
|
76
83
|
runtime-health runtime-decision runtime-launch
|
|
@@ -81,7 +88,7 @@ ONE bin: mm-harness is the only command. No per-platform binaries \u2014 platfor
|
|
|
81
88
|
auto-detected, the positional target forces it (mm-harness launch ios), and
|
|
82
89
|
platform-specific needs are FLAGS on the same command (e.g. --sidebar, --full-build).
|
|
83
90
|
Human happy path = the bare command; agents add depth via flags (--json, --target, ports).
|
|
84
|
-
See
|
|
91
|
+
See docs/MENTAL-MODEL.md (overview) and docs/CLI-SPEC.md (full contract).
|
|
85
92
|
`);
|
|
86
93
|
}
|
|
87
94
|
async function main(argv) {
|
|
@@ -104,7 +111,7 @@ async function main(argv) {
|
|
|
104
111
|
const target = targetPath(options);
|
|
105
112
|
const adapterRaw = (typeof options["adapter"] === "string" ? options["adapter"] : void 0) ?? (typeof options["platform"] === "string" ? options["platform"] : void 0) ?? detectAdapter(target);
|
|
106
113
|
if (adapterRaw === "mobile" || adapterRaw === "extension" || adapterRaw === "core") {
|
|
107
|
-
await handleHarness(["install", "--platform", adapterRaw, "--target", target
|
|
114
|
+
await handleHarness(["install", "--platform", adapterRaw, "--target", target]);
|
|
108
115
|
}
|
|
109
116
|
} catch {
|
|
110
117
|
}
|
|
@@ -126,7 +133,6 @@ async function main(argv) {
|
|
|
126
133
|
if (command === "recipe-quality") return handleRecipeQuality(argv.slice(1));
|
|
127
134
|
if (command === "check") return handleCheck(argv.slice(1));
|
|
128
135
|
if (command === "checklist") return handleChecklist(argv.slice(1));
|
|
129
|
-
if (command === "last") return handleLast(parseArgs(argv.slice(1), command));
|
|
130
136
|
const handler = COMMANDS[command];
|
|
131
137
|
if (!handler) throw new Error(`Unknown command: ${command}`);
|
|
132
138
|
return handler(parseArgs(argv.slice(1), command));
|
package/dist/commands/call.js
CHANGED
|
@@ -6,7 +6,7 @@ import { loadActionManifest } from "../manifest.js";
|
|
|
6
6
|
import { importRecipeProtocol } from "../paths.js";
|
|
7
7
|
import { recipeRunning } from "../heal-bounds.js";
|
|
8
8
|
import { color } from "../cli-color.js";
|
|
9
|
-
import {
|
|
9
|
+
import { EXIT } from "./shared.js";
|
|
10
10
|
import { describeManifestActions, fuzzyResolveActions } from "./manifest.js";
|
|
11
11
|
import {
|
|
12
12
|
parseArgs,
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
optionString,
|
|
16
16
|
resolveAdapter,
|
|
17
17
|
runtimeOptionsFromCli,
|
|
18
|
-
shellQuote,
|
|
19
18
|
usageError
|
|
20
19
|
} from "./parse-args.js";
|
|
21
20
|
import { getAdapterSurface } from "../adapters/surface.js";
|
|
@@ -33,7 +32,6 @@ import {
|
|
|
33
32
|
validateRecipeAdapterAware
|
|
34
33
|
} from "./run-engine.js";
|
|
35
34
|
import { acquireCheckoutLock } from "../checkout-lock.js";
|
|
36
|
-
import { formatRunDiagnosticsForHuman, readRunDiagnosticsDocument } from "../run-diagnostics.js";
|
|
37
35
|
async function handleCall(argv) {
|
|
38
36
|
if (argv.includes("--list")) {
|
|
39
37
|
const { options: options2 } = parseArgs(argv, "call");
|
|
@@ -61,42 +59,25 @@ async function handleCall(argv) {
|
|
|
61
59
|
}
|
|
62
60
|
const message = `call requires <action>. Example: ${example}`;
|
|
63
61
|
const userAction = `${example} # see the vocabulary: ${discovery}`;
|
|
64
|
-
if (json) console.log(JSON.stringify({
|
|
65
|
-
schemaVersion: 1,
|
|
66
|
-
command: "call",
|
|
67
|
-
status: "fail",
|
|
68
|
-
error: { code: "CLI_MISSING_POSITIONAL", message, userAction },
|
|
69
|
-
exitCode: EXIT.usage
|
|
70
|
-
}, null, 2));
|
|
62
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", error: { code: "USAGE", message, userAction } }, null, 2));
|
|
71
63
|
else console.error(`${message}
|
|
72
64
|
See the vocabulary: ${discovery}`);
|
|
73
65
|
return EXIT.usage;
|
|
74
66
|
}
|
|
75
67
|
const { adapter, target } = resolveAdapter(options);
|
|
76
|
-
if (!fs.existsSync(target)) {
|
|
77
|
-
return usageOut(
|
|
78
|
-
json,
|
|
79
|
-
"call",
|
|
80
|
-
`target does not exist: ${target}`,
|
|
81
|
-
"pass --target <metamask-checkout> pointing to an existing checkout"
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
68
|
getAdapterSurface(adapter).resolveSlotPorts(target);
|
|
85
69
|
const dtResult = applyDeviceTargeting("call", adapter, options, { gate: true, rerun: "" });
|
|
86
70
|
if ("code" in dtResult) {
|
|
87
|
-
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, error: { code: dtResult.code, message: dtResult.message
|
|
88
|
-
else console.error(`\u2717 call: ${dtResult.message}
|
|
89
|
-
Next: ${dtResult.userAction}`);
|
|
71
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, error: { code: dtResult.code, message: dtResult.message } }, null, 2));
|
|
72
|
+
else console.error(`\u2717 call: ${dtResult.message}`);
|
|
90
73
|
return EXIT.usage;
|
|
91
74
|
}
|
|
92
75
|
if (recipeRunning(target)) {
|
|
93
76
|
const msg = "a recipe is currently running \u2014 refusing to start while another recipe executes.";
|
|
94
|
-
const userAction = `inspect the checkout state with: mm-harness status --target ${shellQuote(target)} --json; retry after the active recipe finishes`;
|
|
95
77
|
if (json) {
|
|
96
|
-
console.log(JSON.stringify({ schemaVersion: 1, command: "call", status: "fail", recoverable: false, error: { code: "RECIPE_RUNNING", message: msg
|
|
78
|
+
console.log(JSON.stringify({ schemaVersion: 1, command: "call", status: "fail", recoverable: false, error: { code: "RECIPE_RUNNING", message: msg } }, null, 2));
|
|
97
79
|
} else {
|
|
98
|
-
console.error(`\u2717 mm-harness call: ${msg}
|
|
99
|
-
Next: ${userAction}`);
|
|
80
|
+
console.error(`\u2717 mm-harness call: ${msg}`);
|
|
100
81
|
}
|
|
101
82
|
return EXIT.bounded;
|
|
102
83
|
}
|
|
@@ -106,19 +87,16 @@ async function handleCall(argv) {
|
|
|
106
87
|
const names = getRecipeActionManifestActionNames(manifest);
|
|
107
88
|
const resolution = resolveActionName(shortName, names);
|
|
108
89
|
if (resolution.status === "unknown") {
|
|
109
|
-
const message =
|
|
110
|
-
|
|
111
|
-
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, action: shortName, error: { code: "ACTION_UNKNOWN", message
|
|
112
|
-
else console.error(
|
|
113
|
-
Next: ${userAction}`);
|
|
90
|
+
const message = `\u2717 call: unknown action "${shortName}" for the ${adapter} adapter.
|
|
91
|
+
See the vocabulary: mm-harness actions --adapter ${adapter} --json`;
|
|
92
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, action: shortName, error: { code: "ACTION_UNKNOWN", message } }, null, 2));
|
|
93
|
+
else console.error(message);
|
|
114
94
|
return EXIT.usage;
|
|
115
95
|
}
|
|
116
96
|
if (resolution.status === "ambiguous") {
|
|
117
|
-
const message =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
else console.error(`\u2717 call: ${message}
|
|
121
|
-
Next: ${userAction}`);
|
|
97
|
+
const message = `\u2717 call: "${shortName}" is ambiguous: ${resolution.candidates.join(", ")} \u2014 use the full name.`;
|
|
98
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, action: shortName, error: { code: "ACTION_AMBIGUOUS", message, candidates: resolution.candidates } }, null, 2));
|
|
99
|
+
else console.error(message);
|
|
122
100
|
return EXIT.usage;
|
|
123
101
|
}
|
|
124
102
|
const resolvedAction = resolution.resolved;
|
|
@@ -135,19 +113,19 @@ async function handleCall(argv) {
|
|
|
135
113
|
const recipe = synthesizeOneNodeRecipe(resolvedAction, args);
|
|
136
114
|
const validation = await validateRecipeAdapterAware(recipe, manifest);
|
|
137
115
|
if (validation.status === "invalid") {
|
|
138
|
-
const message =
|
|
139
|
-
|
|
140
|
-
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, action: shortName, resolvedAction, args, findings: validation.findings, error: { code: "RECIPE_VALIDATION_FAILED", message, userAction } }, null, 2));
|
|
116
|
+
const message = `\u2717 call ${resolvedAction}: recipe validation failed`;
|
|
117
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", adapter, action: shortName, resolvedAction, args, findings: validation.findings, error: { code: "RECIPE_VALIDATION_FAILED", message } }, null, 2));
|
|
141
118
|
else {
|
|
142
|
-
console.error(
|
|
119
|
+
console.error(message);
|
|
143
120
|
for (const finding of validation.findings) console.error(` ${finding.code} ${finding.path} \u2014 ${finding.message}`);
|
|
144
|
-
console.error(` Next: ${userAction}`);
|
|
145
121
|
}
|
|
146
122
|
return EXIT.validation;
|
|
147
123
|
}
|
|
148
124
|
const lock = acquireCheckoutLock(target, "call");
|
|
149
125
|
if ("message" in lock) {
|
|
150
|
-
|
|
126
|
+
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "call", status: "fail", recoverable: false, error: { code: "SANDBOX_BUSY", message: lock.message } }, null, 2));
|
|
127
|
+
else console.error(`\u2717 mm-harness call: ${lock.message}`);
|
|
128
|
+
return EXIT.bounded;
|
|
151
129
|
}
|
|
152
130
|
try {
|
|
153
131
|
const prepared = await prepareHeal(adapter, target, options, json, {
|
|
@@ -176,7 +154,6 @@ async function handleCall(argv) {
|
|
|
176
154
|
);
|
|
177
155
|
if (violation !== null) return emitHealViolation(json, "call", result, violation, state, adapter);
|
|
178
156
|
const callOutput = readCallOutput(result.tracePath);
|
|
179
|
-
const failureUserAction = `mm-harness last --target ${shellQuote(target)} --json`;
|
|
180
157
|
if (json) {
|
|
181
158
|
console.log(
|
|
182
159
|
JSON.stringify(
|
|
@@ -191,13 +168,10 @@ async function handleCall(argv) {
|
|
|
191
168
|
summaryPath: result.summaryPath,
|
|
192
169
|
tracePath: result.tracePath,
|
|
193
170
|
artifactManifestPath: result.artifactManifestPath,
|
|
194
|
-
...result.diagnosticsPath ? { diagnosticsPath: result.diagnosticsPath } : {},
|
|
195
|
-
...result.sideFindings ? { sideFindings: result.sideFindings } : {},
|
|
196
171
|
...callOutput !== void 0 ? { output: callOutput } : {},
|
|
197
172
|
recovered: state.recovered,
|
|
198
173
|
mutations: state.mutations,
|
|
199
|
-
exitCode: result.status === "pass" ? EXIT.ok : EXIT.runtime
|
|
200
|
-
...result.status === "fail" ? { error: { code: "ACTION_EXECUTION_FAILED", message: `${resolvedAction} failed; inspect the persisted result and evidence paths`, userAction: failureUserAction } } : {}
|
|
174
|
+
exitCode: result.status === "pass" ? EXIT.ok : EXIT.runtime
|
|
201
175
|
},
|
|
202
176
|
null,
|
|
203
177
|
2
|
|
@@ -208,19 +182,11 @@ async function handleCall(argv) {
|
|
|
208
182
|
Result:
|
|
209
183
|
${formatCallOutput(callOutput)}` : "";
|
|
210
184
|
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
211
|
-
const diagnostics = formatRunDiagnosticsForHuman(
|
|
212
|
-
readRunDiagnosticsDocument(result.diagnosticsPath),
|
|
213
|
-
adapter
|
|
214
|
-
);
|
|
215
|
-
const renderedDiagnostics = `${out("label", "Diagnostics:")}
|
|
216
|
-
${diagnostics.map((line) => ` ${line}`).join("\n")}
|
|
217
|
-
`;
|
|
218
185
|
console.log(
|
|
219
186
|
`${out("label", "call")} ${out("cmd", resolvedAction)}: ${out(result.status === "pass" ? "ok" : "err", result.status)}${rendered ? `
|
|
220
187
|
${out("label", "Result:")}
|
|
221
188
|
${formatCallOutput(callOutput)}` : ""}
|
|
222
|
-
|
|
223
|
-
Next: ${failureUserAction}` : "")
|
|
189
|
+
${out("label", "Artifacts:")} ${out("path", result.artifactManifestPath)}`
|
|
224
190
|
);
|
|
225
191
|
}
|
|
226
192
|
return result.status === "pass" ? EXIT.ok : EXIT.runtime;
|
|
@@ -354,7 +320,7 @@ function parseCallValue(value) {
|
|
|
354
320
|
const number = Number(value);
|
|
355
321
|
if (Number.isFinite(number)) return number;
|
|
356
322
|
}
|
|
357
|
-
if (value.startsWith("[") && value.endsWith("]") || value.startsWith("{") && value.endsWith("}")
|
|
323
|
+
if (value.startsWith("[") && value.endsWith("]") || value.startsWith("{") && value.endsWith("}")) {
|
|
358
324
|
try {
|
|
359
325
|
return JSON.parse(value);
|
|
360
326
|
} catch {
|
package/dist/commands/check.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { detectAdapter } from "../harness.js";
|
|
5
5
|
import { EXIT, usageOut } from "./shared.js";
|
|
6
|
-
import { optionFlag, optionString, parseArgs
|
|
6
|
+
import { optionFlag, optionString, parseArgs } from "./parse-args.js";
|
|
7
7
|
const USAGE = "mm-harness check diff [--fix] [--target <repo>] [--adapter mobile|extension|core] [--base <ref>] [--profile fast|full] [--artifacts-dir <dir>] [--json]";
|
|
8
8
|
const JS_TS_EXT_RE = /\.(?:c|m)?jsx?$|\.tsx?$/u;
|
|
9
9
|
const FORMAT_EXT_RE = /\.(?:c|m)?jsx?$|\.tsx?$|\.json$|\.md$|\.css$|\.scss$|\.ya?ml$/u;
|
|
@@ -111,7 +111,6 @@ async function handleCheck(argv) {
|
|
|
111
111
|
const status = [...fixes, ...checks].some((check) => check.status === "fail") ? "fail" : "pass";
|
|
112
112
|
const exitCode = status === "pass" ? EXIT.ok : EXIT.validation;
|
|
113
113
|
const guidance = policyFailed ? ["Remove the newly added eslint-disable directive(s). Fix the underlying violation with existing named styles, project tokens, or another established source pattern; then rerun mm-harness check diff [--fix]. Other checks were skipped until this policy failure is removed."] : checks.some((check) => check.id === "eslint" && check.status === "fail") ? ["Fix the reported violations in changed source using existing project patterns and tokens; do not suppress rules, hide violations behind indirection, weaken configuration, or bypass check diff. Then rerun mm-harness check diff [--fix]."] : [];
|
|
114
|
-
const failureUserAction = guidance[0] ?? `inspect ${path.join(artifactsDir, "validation-summary.json")}, fix the failing check, then retry: mm-harness check diff --target ${shellQuote(target)} --base ${shellQuote(baseRef)} --profile ${profile} --json`;
|
|
115
114
|
const summary = {
|
|
116
115
|
schemaVersion: 1,
|
|
117
116
|
command: "check",
|
|
@@ -130,7 +129,6 @@ async function handleCheck(argv) {
|
|
|
130
129
|
fixes,
|
|
131
130
|
checks,
|
|
132
131
|
guidance,
|
|
133
|
-
...status === "fail" ? { error: { code: "CHECK_DIFF_FAILED", message: "one or more bounded checks failed", userAction: failureUserAction } } : {},
|
|
134
132
|
artifacts: {
|
|
135
133
|
dir: artifactsDir,
|
|
136
134
|
summaryJson: path.join(artifactsDir, "validation-summary.json"),
|
|
@@ -520,14 +518,10 @@ function renderHuman(summary) {
|
|
|
520
518
|
if (check.status === "fail" && check.logPath) console.log(` log: ${check.logPath}`);
|
|
521
519
|
}
|
|
522
520
|
for (const item of summary.guidance) console.log(` Next: ${item}`);
|
|
523
|
-
if (summary.guidance.length === 0 && summary.status === "fail" && summary.error?.userAction) {
|
|
524
|
-
console.log(` Next: ${summary.error.userAction}`);
|
|
525
|
-
}
|
|
526
521
|
console.log(`Artifacts: ${summary.artifacts.summaryJson}`);
|
|
527
522
|
}
|
|
528
523
|
function failEnvelope(input) {
|
|
529
524
|
const exitCode = EXIT.usage;
|
|
530
|
-
const userAction = USAGE;
|
|
531
525
|
const body = {
|
|
532
526
|
schemaVersion: 1,
|
|
533
527
|
command: "check",
|
|
@@ -540,11 +534,11 @@ function failEnvelope(input) {
|
|
|
540
534
|
baseRef: input.baseRef,
|
|
541
535
|
baseSource: "unknown",
|
|
542
536
|
artifacts: { dir: input.artifactsDir },
|
|
543
|
-
error: { code: input.code, message: input.message
|
|
537
|
+
error: { code: input.code, message: input.message }
|
|
544
538
|
};
|
|
545
539
|
if (input.json) console.log(JSON.stringify(body, null, 2));
|
|
546
540
|
else console.error(`\u2717 check diff: ${input.message}
|
|
547
|
-
Next: ${
|
|
541
|
+
Next: ${USAGE}`);
|
|
548
542
|
return exitCode;
|
|
549
543
|
}
|
|
550
544
|
export {
|