@deeeed/metamask-harness 0.78.0 → 0.80.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 +55 -0
- package/README.md +1 -1
- package/adapters/extension/ensure-browser.sh +2 -2
- package/adapters/extension/inject.mjs +0 -1
- package/adapters/extension/launch-browser.cjs +39 -613
- package/adapters/extension/readiness.mjs +2 -5
- package/adapters/extension/reattach.sh +2 -2
- package/adapters/extension/stamp-runtime-title.cjs +1 -1
- package/adapters/manifest.json +2 -34
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
- package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
- package/adapters/mobile/inject.sh +16 -0
- package/adapters/mobile/launch-console-forwarder.cjs +1 -2
- package/adapters/mobile/open-device.sh +2 -2
- package/adapters/mobile/reload-app.mjs +4 -4
- package/adapters/mobile/reset-app-data.sh +1 -1
- package/adapters/mobile/start-console-forwarder.sh +3 -2
- package/adapters/mobile/start-metro.sh +17 -12
- package/adapters/mobile/wait-for-bridge.cjs +2 -2
- package/adapters/shared/ensure-runner-deps.sh +2 -2
- package/adapters/shared/harness-source-fingerprint.mjs +13 -8
- package/adapters/shared/open-debug.mjs +2 -2
- package/adapters/shared/runner-deps-ready.mjs +1 -1
- package/adapters/shared/sync-wallet-fixture.sh +8 -5
- package/adapters/terminal/cleanup.mjs +2 -1
- package/adapters/terminal/inject.mjs +2 -1
- package/adapters/terminal/lib/cdp-client.mjs +1 -1
- package/bin/mm-harness +9 -28
- package/dist/adapters/checkout-files.js +16 -0
- package/dist/adapters/console-capture.js +14 -1
- package/dist/adapters/core/actions.js +4 -3
- package/dist/adapters/core/overlay.js +1 -1
- package/dist/adapters/core/readiness.js +28 -1
- package/dist/adapters/core/run.js +3 -3
- package/dist/adapters/core/surface.js +22 -5
- package/dist/adapters/extension/browser-pid.js +56 -0
- package/dist/adapters/extension/console-capture.js +1 -1
- package/dist/adapters/extension/doctor-checks.js +1 -1
- package/dist/adapters/extension/ensure-ready.js +5 -4
- package/dist/adapters/extension/extension-id.js +1 -1
- package/dist/adapters/extension/feature-flags.js +1 -1
- package/dist/adapters/extension/fixtures.js +1 -2
- package/dist/adapters/extension/launch.js +4 -5
- package/dist/adapters/extension/launched-browser.js +1 -2
- package/dist/adapters/extension/network-observer.js +1 -1
- package/dist/adapters/extension/overlay.js +1 -1
- package/dist/adapters/extension/product-config.js +1 -1
- package/dist/adapters/extension/readiness.js +1 -1
- package/dist/adapters/extension/reload.js +1 -1
- package/dist/adapters/extension/run.js +24 -10
- package/dist/adapters/extension/runtime-decision.js +3 -2
- package/dist/adapters/extension/runtime.js +9 -5
- package/dist/adapters/extension/surface.js +20 -5
- package/dist/adapters/extension/verify.js +2 -2
- package/dist/adapters/harness-freshness.js +2 -1
- package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
- package/dist/adapters/mobile/app-identity.js +10 -0
- package/dist/adapters/mobile/doctor-checks.js +1 -1
- package/dist/adapters/mobile/fixtures.js +1 -1
- package/dist/adapters/mobile/launch.js +27 -28
- package/dist/adapters/mobile/lifecycle.js +1 -1
- package/dist/adapters/mobile/metro-env.js +9 -59
- package/dist/adapters/mobile/performance-observer.js +1 -1
- package/dist/adapters/mobile/prepare-target.js +1 -1
- package/dist/adapters/mobile/prepare.js +4 -5
- package/dist/adapters/mobile/provision.js +1 -1
- package/dist/adapters/mobile/readiness.js +1 -1
- package/dist/adapters/mobile/recording-target.js +15 -0
- package/dist/adapters/mobile/release-artifact-state.js +1 -1
- package/dist/adapters/mobile/reload.js +1 -1
- package/dist/adapters/mobile/run.js +9 -8
- package/dist/adapters/mobile/runtime-decision.js +5 -7
- package/dist/adapters/mobile/source-freshness.js +22 -126
- package/dist/adapters/mobile/surface.js +31 -6
- package/dist/adapters/mobile/ui.js +16 -5
- package/dist/adapters/mobile/verify.js +2 -3
- package/dist/adapters/performance/cdp-trace.js +1 -1
- package/dist/adapters/performance/device-platform.js +1 -1
- package/dist/adapters/registry.js +2 -14
- package/dist/adapters/slot-ports.js +1 -1
- package/dist/adapters/surface.js +1 -0
- package/dist/adapters/terminal/console-capture.js +1 -1
- package/dist/adapters/terminal/launch.js +1 -1
- package/dist/adapters/terminal/run.js +25 -2
- package/dist/adapters/terminal/surface.js +12 -4
- package/dist/adapters.js +8 -4
- package/dist/advisor-policy.js +1 -1
- package/dist/checkout-view.js +1 -2
- package/dist/cli.js +28 -15
- package/dist/command-contract.js +1 -26
- package/dist/commands/call.js +10 -24
- package/dist/commands/check.js +3 -4
- package/dist/commands/checklist.js +1 -1
- package/dist/commands/completion-candidates.js +2 -10
- package/dist/commands/config.js +2 -2
- package/dist/commands/core-readiness.js +12 -7
- package/dist/commands/debug.js +3 -3
- package/dist/commands/device-target.js +5 -5
- package/dist/commands/doctor.js +2 -14
- package/dist/commands/domain.js +2 -2
- package/dist/commands/ensure-ready.js +1 -7
- package/dist/commands/fixtures.js +5 -6
- package/dist/commands/help.js +3 -4
- package/dist/commands/launch/device.js +10 -17
- package/dist/commands/launch/extension.js +3 -9
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/commands/list-executables.js +1 -3
- package/dist/commands/logs.js +3 -3
- package/dist/commands/manifest.js +1 -12
- package/dist/commands/mobile-device-view.js +4 -2
- package/dist/commands/overlay.js +110 -0
- package/dist/commands/pin.js +1 -1
- package/dist/commands/pr-body.js +2 -2
- package/dist/commands/prepare.js +6 -11
- package/dist/commands/provision.js +3 -12
- package/dist/commands/recipe-advice.js +1 -2
- package/dist/commands/recipe-quality.js +2 -3
- package/dist/commands/resolve-extension.js +1 -7
- package/dist/commands/review.js +4 -4
- package/dist/commands/run-engine.js +12 -51
- package/dist/commands/run.js +4 -25
- package/dist/commands/runtime-decision.js +1 -6
- package/dist/commands/runtime-health.js +1 -7
- package/dist/commands/runtime-launch.js +1 -13
- package/dist/commands/self-test.js +2 -6
- package/dist/commands/status-watch.js +1 -2
- package/dist/commands/status.js +2 -5
- package/dist/commands/task-init.js +2 -3
- package/dist/commands/tutorial.js +2 -2
- package/dist/commands/update.js +1 -1
- package/dist/completions-cache.js +1 -1
- package/dist/console-classifier.js +19 -0
- package/dist/discovery.js +1 -1
- package/dist/doctor.js +3 -4
- package/dist/funding-execution-context.js +15 -1
- package/dist/host.js +32 -0
- package/dist/index.js +1 -6
- package/dist/live-tsx.js +49 -0
- package/dist/mm-harness-cli.js +2 -4
- package/dist/network-observation.js +4 -4
- package/dist/paths.js +48 -78
- package/dist/performance-observation.js +2 -2
- package/dist/progress.js +1 -1
- package/dist/recipe-assistance-context.js +2 -4
- package/dist/recipe-security.js +2 -59
- package/dist/review/knowledge.js +3 -3
- package/dist/runner.js +5 -3
- package/dist/runtime-context.js +1 -1
- package/dist/task-view.js +3 -4
- package/docs/CONTRIBUTING.md +3 -3
- package/docs/cli-contract.md +2 -2
- package/docs/feature-guard.md +90 -0
- package/library/README.md +3 -1
- package/library/actions/core/perps/_controller.mjs +153 -130
- package/library/actions/core/perps/quote_fees.mjs +58 -0
- package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
- package/library/actions/core/wallet/list_accounts.mjs +14 -57
- package/library/actions/extension/feature_flags/state.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +2 -8
- package/library/actions/harness-exports.mjs +1 -6
- package/library/actions/mobile/app/network-control.mjs +6 -8
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +8 -8
- package/library/actions/mobile/platform/native-session-name.mjs +21 -1
- package/library/actions/mobile/platform/native-session.mjs +3 -4
- package/library/actions/mobile/platform/observe-ui.mjs +12 -6
- package/library/actions/mobile/ui/native-navigation.mjs +4 -3
- package/library/actions/mobile/wallet/native-ui.mjs +3 -2
- package/library/actions/shared/analytics/_adapter.mjs +1 -1
- package/library/actions/shared/console-findings.mjs +1 -1
- package/library/actions/terminal/platform/runtime.mjs +2 -1
- package/library/manifests/core.action-manifest.json +83 -13
- package/package.json +8 -8
- package/adapters/extension/lib/slot-title.cjs +0 -292
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
- package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
- package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
- package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
- package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
- package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
- package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
- package/adapters/mobile/coalesce-metro-log.cjs +0 -24
- package/adapters/mobile/launch-metro.cjs +0 -77
- package/adapters/mobile/metro-config.cjs +0 -98
- package/adapters/mobile/metro-log-generation.cjs +0 -106
- package/dist/adapters/mobile/frame-metrics.js +0 -118
- package/dist/adapters/mobile/video-recorder.js +0 -289
- package/dist/artifact-files.js +0 -92
- package/dist/checkout-lock.js +0 -149
- package/dist/cli-color.js +0 -98
- package/dist/command-journal.js +0 -308
- package/dist/commands/last.js +0 -60
- package/dist/commands/launch/index.js +0 -109
- package/dist/commands/parse-args.js +0 -279
- package/dist/commands/reload.js +0 -41
- package/dist/commands/run-report.js +0 -241
- package/dist/commands/shared.js +0 -323
- package/dist/commands/stop.js +0 -60
- package/dist/devices.js +0 -100
- package/dist/execution-provenance.js +0 -332
- package/dist/harness.js +0 -451
- package/dist/heal-bounds.js +0 -137
- package/dist/json-stream.js +0 -60
- package/dist/leaf-invoke.js +0 -19
- package/dist/library-provenance.js +0 -33
- package/dist/live-adapter-contract.js +0 -380
- package/dist/recording-target.js +0 -120
- package/dist/run-diagnostics.js +0 -364
- package/dist/run-recording.js +0 -481
- package/dist/runtime-proof.js +0 -90
- package/library/actions/mobile/platform/tool-paths.mjs +0 -123
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { execFileSync } from "node:child_process";
|
|
4
|
-
import { getAdapterSurface } from "../adapters/surface.js";
|
|
5
|
-
import { isRecord } from "./parse-args.js";
|
|
6
|
-
function writeRunReport(result) {
|
|
7
|
-
const summaryPath = String(result.summaryPath);
|
|
8
|
-
const tracePath = String(result.tracePath);
|
|
9
|
-
const artifactManifestPath = String(result.artifactManifestPath);
|
|
10
|
-
const artifactsDir = path.dirname(summaryPath);
|
|
11
|
-
const summary = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
|
|
12
|
-
const trace = JSON.parse(fs.readFileSync(tracePath, "utf8"));
|
|
13
|
-
const entries = Array.isArray(trace.entries) ? trace.entries : [];
|
|
14
|
-
const lines = renderRunReport(summary, entries);
|
|
15
|
-
const reportPath = path.join(artifactsDir, "report.md");
|
|
16
|
-
fs.writeFileSync(reportPath, `${lines.join("\n")}
|
|
17
|
-
`);
|
|
18
|
-
indexRunReportArtifact(artifactManifestPath);
|
|
19
|
-
return {
|
|
20
|
-
path: reportPath,
|
|
21
|
-
preview: lines.filter((line) => line.startsWith("- ")).slice(0, 6).map((line) => line.slice(2))
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function indexProductProvenanceArtifact(artifactManifestPath, target, adapter, browser = null) {
|
|
25
|
-
const artifactsDir = path.dirname(artifactManifestPath);
|
|
26
|
-
let gitRef = "unknown";
|
|
27
|
-
let branch = "unknown";
|
|
28
|
-
let dirty = true;
|
|
29
|
-
try {
|
|
30
|
-
gitRef = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
31
|
-
cwd: target,
|
|
32
|
-
encoding: "utf8",
|
|
33
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
34
|
-
}).trim();
|
|
35
|
-
branch = execFileSync("git", ["branch", "--show-current"], {
|
|
36
|
-
cwd: target,
|
|
37
|
-
encoding: "utf8",
|
|
38
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
39
|
-
}).trim() || "detached";
|
|
40
|
-
dirty = execFileSync(
|
|
41
|
-
"git",
|
|
42
|
-
["status", "--porcelain", "--untracked-files=normal"],
|
|
43
|
-
{
|
|
44
|
-
cwd: target,
|
|
45
|
-
encoding: "utf8",
|
|
46
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
47
|
-
}
|
|
48
|
-
).trim().length > 0;
|
|
49
|
-
} catch {
|
|
50
|
-
}
|
|
51
|
-
const provenance = {
|
|
52
|
-
schemaVersion: 1,
|
|
53
|
-
adapter,
|
|
54
|
-
repository: path.basename(path.resolve(target)),
|
|
55
|
-
git_ref: gitRef,
|
|
56
|
-
branch,
|
|
57
|
-
dirty,
|
|
58
|
-
...browser ? { browser } : {}
|
|
59
|
-
};
|
|
60
|
-
fs.writeFileSync(
|
|
61
|
-
path.join(artifactsDir, "product-provenance.json"),
|
|
62
|
-
`${JSON.stringify(provenance, null, 2)}
|
|
63
|
-
`
|
|
64
|
-
);
|
|
65
|
-
let manifest;
|
|
66
|
-
try {
|
|
67
|
-
manifest = JSON.parse(fs.readFileSync(artifactManifestPath, "utf8"));
|
|
68
|
-
} catch {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (!isRecord(manifest)) return;
|
|
72
|
-
const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
|
|
73
|
-
manifest.artifacts = [
|
|
74
|
-
...artifacts.filter(
|
|
75
|
-
(artifact) => !isRecord(artifact) || artifact.path !== "product-provenance.json"
|
|
76
|
-
),
|
|
77
|
-
{
|
|
78
|
-
path: "product-provenance.json",
|
|
79
|
-
type: "json",
|
|
80
|
-
label: "Product checkout provenance",
|
|
81
|
-
category: "system",
|
|
82
|
-
metadata: {
|
|
83
|
-
adapter,
|
|
84
|
-
git_ref: gitRef,
|
|
85
|
-
branch,
|
|
86
|
-
dirty,
|
|
87
|
-
...browser?.boundTo && browser.boundTo !== "none" ? { browser: browser.browser, browser_version: browser.version, browser_source: browser.source, extension_loading: browser.extensionLoading } : {}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
];
|
|
91
|
-
fs.writeFileSync(
|
|
92
|
-
artifactManifestPath,
|
|
93
|
-
`${JSON.stringify(manifest, null, 2)}
|
|
94
|
-
`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
function resolveTemplatedPort(value, params, recipe) {
|
|
98
|
-
let resolved = value;
|
|
99
|
-
const template = /^\{\{\s*params\.([\w-]+)\s*\}\}$/u.exec(String(value).trim());
|
|
100
|
-
if (template) {
|
|
101
|
-
const name = template[1];
|
|
102
|
-
const schema = isRecord(recipe) && isRecord(recipe.paramsSchema) && isRecord(recipe.paramsSchema.properties) ? recipe.paramsSchema.properties[name] : void 0;
|
|
103
|
-
resolved = isRecord(params) && params[name] !== void 0 ? params[name] : isRecord(schema) ? schema.default : void 0;
|
|
104
|
-
}
|
|
105
|
-
const text = String(resolved ?? "").trim();
|
|
106
|
-
return /^\d+$/u.test(text) ? text : null;
|
|
107
|
-
}
|
|
108
|
-
function recipeCdpPorts(request) {
|
|
109
|
-
const ports = /* @__PURE__ */ new Set();
|
|
110
|
-
const envPort = request.env?.CDP_PORT || request.env?.RECIPE_CDP_PORT;
|
|
111
|
-
if (envPort) ports.add(String(envPort));
|
|
112
|
-
let recipe = request.recipeDocument;
|
|
113
|
-
if (recipe === void 0 && request.recipePath) {
|
|
114
|
-
try {
|
|
115
|
-
recipe = JSON.parse(fs.readFileSync(request.recipePath, "utf8"));
|
|
116
|
-
} catch {
|
|
117
|
-
recipe = void 0;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const workflow = isRecord(recipe) && isRecord(recipe.workflow) ? recipe.workflow : null;
|
|
121
|
-
const nodes = workflow && isRecord(workflow.nodes) ? Object.values(workflow.nodes) : [];
|
|
122
|
-
for (const node of nodes) {
|
|
123
|
-
if (!isRecord(node) || node.cdp_port === void 0 || node.cdp_port === null) continue;
|
|
124
|
-
const port = resolveTemplatedPort(node.cdp_port, request.params, recipe);
|
|
125
|
-
ports.add(port ?? `unresolved ${String(node.cdp_port)}`);
|
|
126
|
-
}
|
|
127
|
-
return [...ports];
|
|
128
|
-
}
|
|
129
|
-
function executedBrowser(adapter, target, artifactManifestPath, ports) {
|
|
130
|
-
const launched = getAdapterSurface(adapter).run?.launchedBrowser;
|
|
131
|
-
if (!launched) return null;
|
|
132
|
-
if (ports.length === 0) return { boundTo: "none", reason: "the run had no known CDP port" };
|
|
133
|
-
if (ports.length > 1) return { boundTo: "none", reason: `the run drove several CDP ports (${ports.join(", ")})` };
|
|
134
|
-
return launched(target, path.dirname(artifactManifestPath), ports[0]);
|
|
135
|
-
}
|
|
136
|
-
function renderRunReport(summary, entries) {
|
|
137
|
-
const lines = [
|
|
138
|
-
"# MetaMask Recipe Run",
|
|
139
|
-
"",
|
|
140
|
-
`Status: ${String(summary.status ?? "unknown")}`,
|
|
141
|
-
`Duration: ${formatDuration(Number(summary.durationMs ?? 0))}`,
|
|
142
|
-
`Nodes: ${Number(summary.passed ?? 0)}/${Number(summary.total ?? entries.length)} passed`
|
|
143
|
-
];
|
|
144
|
-
const sideFindings = isRecord(summary.sideFindings) ? summary.sideFindings : void 0;
|
|
145
|
-
const sideFindingCounts = sideFindings && isRecord(sideFindings.counts) ? sideFindings.counts : void 0;
|
|
146
|
-
const sideFindingTotal = Number(sideFindingCounts?.total ?? 0);
|
|
147
|
-
if (sideFindingTotal > 0) {
|
|
148
|
-
lines.push(
|
|
149
|
-
"",
|
|
150
|
-
"## Side findings",
|
|
151
|
-
`- REVIEW ${sideFindingTotal} distinct application warning/error event(s) (non-blocking; expanded below and stored in diagnostics.json)`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
lines.push("", "## Steps");
|
|
155
|
-
for (const entry of entries) {
|
|
156
|
-
if (!isRecord(entry)) continue;
|
|
157
|
-
const mark = entry.ok === false ? "FAIL" : "PASS";
|
|
158
|
-
const nodeId = String(entry.nodeId ?? "(node)");
|
|
159
|
-
const action = String(entry.action ?? "(action)");
|
|
160
|
-
const duration = formatDuration(Number(entry.durationMs ?? 0));
|
|
161
|
-
const detail = summarizeNodeOutput(entry.output);
|
|
162
|
-
lines.push(`- ${mark} ${nodeId} (${action}, ${duration})${detail ? `: ${detail}` : ""}`);
|
|
163
|
-
}
|
|
164
|
-
return lines;
|
|
165
|
-
}
|
|
166
|
-
function summarizeNodeOutput(output) {
|
|
167
|
-
if (!isRecord(output)) return "";
|
|
168
|
-
const parts = [];
|
|
169
|
-
const preferredKeys = [
|
|
170
|
-
"platform",
|
|
171
|
-
"screen",
|
|
172
|
-
"route",
|
|
173
|
-
"page",
|
|
174
|
-
"network",
|
|
175
|
-
"account",
|
|
176
|
-
"count",
|
|
177
|
-
"matchingCount",
|
|
178
|
-
"proofPath",
|
|
179
|
-
"screenshot",
|
|
180
|
-
"path"
|
|
181
|
-
];
|
|
182
|
-
for (const key of preferredKeys) addPart(parts, labelFor(key), summarizeValue(output[key]));
|
|
183
|
-
const accountState = isRecord(output.accountState) ? output.accountState : void 0;
|
|
184
|
-
if (accountState) {
|
|
185
|
-
addPart(parts, "totalBalance", accountState.totalBalance);
|
|
186
|
-
addPart(parts, "spendable", accountState.spendableBalance);
|
|
187
|
-
addPart(parts, "marginUsed", accountState.marginUsed);
|
|
188
|
-
addPart(parts, "unrealizedPnl", accountState.unrealizedPnl);
|
|
189
|
-
}
|
|
190
|
-
if (parts.length === 0) {
|
|
191
|
-
for (const [key, value] of Object.entries(output)) {
|
|
192
|
-
if (parts.length >= 5) break;
|
|
193
|
-
if (key === "liveAdapter" || key === "artifacts") continue;
|
|
194
|
-
addPart(parts, key, summarizeValue(value));
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return parts.join(", ");
|
|
198
|
-
}
|
|
199
|
-
function labelFor(key) {
|
|
200
|
-
return key === "matchingCount" ? "matching" : key === "proofPath" ? "proof" : key;
|
|
201
|
-
}
|
|
202
|
-
function summarizeValue(value) {
|
|
203
|
-
if (typeof value === "string") return shortenAddress(value);
|
|
204
|
-
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
205
|
-
if (Array.isArray(value)) return `${value.length} item${value.length === 1 ? "" : "s"}`;
|
|
206
|
-
return void 0;
|
|
207
|
-
}
|
|
208
|
-
function addPart(parts, label, value) {
|
|
209
|
-
if (value === void 0 || value === null || value === "") return;
|
|
210
|
-
parts.push(`${label}=${String(value)}`);
|
|
211
|
-
}
|
|
212
|
-
function shortenAddress(value) {
|
|
213
|
-
return /^0x[a-fA-F0-9]{40}$/u.test(value) ? `${value.slice(0, 6)}...${value.slice(-4)}` : value;
|
|
214
|
-
}
|
|
215
|
-
function formatDuration(ms) {
|
|
216
|
-
if (!Number.isFinite(ms) || ms <= 0) return "0ms";
|
|
217
|
-
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
218
|
-
return `${(ms / 1e3).toFixed(ms < 1e4 ? 1 : 0)}s`;
|
|
219
|
-
}
|
|
220
|
-
function indexRunReportArtifact(artifactManifestPath) {
|
|
221
|
-
let manifest;
|
|
222
|
-
try {
|
|
223
|
-
manifest = JSON.parse(fs.readFileSync(artifactManifestPath, "utf8"));
|
|
224
|
-
} catch {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (!isRecord(manifest)) return;
|
|
228
|
-
const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
|
|
229
|
-
manifest.artifacts = [
|
|
230
|
-
...artifacts.filter((artifact) => !isRecord(artifact) || artifact.path !== "report.md"),
|
|
231
|
-
{ path: "report.md", type: "report", label: "Human run report", category: "system" }
|
|
232
|
-
];
|
|
233
|
-
fs.writeFileSync(artifactManifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
234
|
-
`);
|
|
235
|
-
}
|
|
236
|
-
export {
|
|
237
|
-
executedBrowser,
|
|
238
|
-
indexProductProvenanceArtifact,
|
|
239
|
-
recipeCdpPorts,
|
|
240
|
-
writeRunReport
|
|
241
|
-
};
|
package/dist/commands/shared.js
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import { trackCheckoutChild } from "../checkout-lock.js";
|
|
2
|
-
import { recordCommandOutput, recordCommandStage } from "../command-journal.js";
|
|
3
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
4
|
-
import { StringDecoder } from "node:string_decoder";
|
|
5
|
-
import { OperationOutputTail } from "@farmslot/recipe-harness/runtime/operation";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { detectAdapter } from "../harness.js";
|
|
8
|
-
import { colorHumanMessage } from "../cli-color.js";
|
|
9
|
-
import { resolveLeafInvoke, shellLeafMissing } from "../leaf-invoke.js";
|
|
10
|
-
import { adapterRegistry } from "../adapters/registry.js";
|
|
11
|
-
const EXIT = { ok: 0, runtime: 1, usage: 2, infra: 3, bounded: 4, validation: 5 };
|
|
12
|
-
function writeInteractiveProgress(json, message, {
|
|
13
|
-
stdoutIsTTY = Boolean(process.stdout.isTTY),
|
|
14
|
-
stream = process.stderr
|
|
15
|
-
} = {}) {
|
|
16
|
-
if (json || !stdoutIsTTY) return false;
|
|
17
|
-
stream.write(`${colorHumanMessage(message, { stream })}
|
|
18
|
-
`);
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
function parseFlags(argv, booleans) {
|
|
22
|
-
const positional = [];
|
|
23
|
-
const options = {};
|
|
24
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
25
|
-
const arg = argv[i];
|
|
26
|
-
if (!arg.startsWith("--")) {
|
|
27
|
-
positional.push(arg);
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const body = arg.slice(2);
|
|
31
|
-
const eq = body.indexOf("=");
|
|
32
|
-
const rawKey = eq === -1 ? body : body.slice(0, eq);
|
|
33
|
-
const inline = eq === -1 ? void 0 : body.slice(eq + 1);
|
|
34
|
-
const key = rawKey.replace(/-([a-z])/gu, (_, c) => c.toUpperCase());
|
|
35
|
-
if (booleans.has(key)) {
|
|
36
|
-
options[key] = inline === void 0 ? true : inline !== "false";
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (inline !== void 0) {
|
|
40
|
-
options[key] = inline;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
const next = argv[i + 1];
|
|
44
|
-
if (next === void 0 || next.startsWith("--")) {
|
|
45
|
-
options[key] = true;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
options[key] = next;
|
|
49
|
-
i += 1;
|
|
50
|
-
}
|
|
51
|
-
return { positional, options };
|
|
52
|
-
}
|
|
53
|
-
function str(options, key) {
|
|
54
|
-
const value = options[key];
|
|
55
|
-
return typeof value === "string" ? value : void 0;
|
|
56
|
-
}
|
|
57
|
-
function flag(options, key) {
|
|
58
|
-
return options[key] === true;
|
|
59
|
-
}
|
|
60
|
-
function targetOf(options) {
|
|
61
|
-
return path.resolve(str(options, "target") ?? process.cwd());
|
|
62
|
-
}
|
|
63
|
-
function resolveAdapter(options, target, hint) {
|
|
64
|
-
const platform = str(options, "platform");
|
|
65
|
-
const explicit = str(options, "adapter") ?? (platform === "ios" || platform === "android" ? "mobile" : platform);
|
|
66
|
-
if (explicit && adapterRegistry.has(explicit)) return explicit;
|
|
67
|
-
if (hint) return hint;
|
|
68
|
-
return detectAdapter(target);
|
|
69
|
-
}
|
|
70
|
-
function scriptOverride(scriptPath) {
|
|
71
|
-
const stem = path.basename(scriptPath).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase();
|
|
72
|
-
return process.env[`MM_HARNESS_SCRIPT_BIN_${stem}`];
|
|
73
|
-
}
|
|
74
|
-
function spawnScript(script, args, cwd, json, env) {
|
|
75
|
-
const isNodeScript = script === process.execPath && args.length > 0;
|
|
76
|
-
const stem = isNodeScript ? path.basename(args[0]).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase() : path.basename(script).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase();
|
|
77
|
-
const override = process.env[`MM_HARNESS_SCRIPT_BIN_${stem}`];
|
|
78
|
-
const bin = override ?? script;
|
|
79
|
-
const directArgs = override !== void 0 && isNodeScript ? args.slice(1) : args;
|
|
80
|
-
if (shellLeafMissing(bin)) {
|
|
81
|
-
const message = `leaf could not start: ${path.basename(bin)} (ENOENT)
|
|
82
|
-
Next: reinstall mm-harness (npm i -g @deeeed/metamask-harness) \u2014 the shell leaf is missing or not executable`;
|
|
83
|
-
process.stderr.write(`${message}
|
|
84
|
-
`);
|
|
85
|
-
return { status: 1, output: message };
|
|
86
|
-
}
|
|
87
|
-
const { bin: invokeBin, args: spawnArgs } = resolveLeafInvoke(bin, directArgs);
|
|
88
|
-
recordCommandStage(path.basename(isNodeScript ? args[0] : script));
|
|
89
|
-
const result = spawnSync(invokeBin, spawnArgs, {
|
|
90
|
-
cwd,
|
|
91
|
-
encoding: "utf8",
|
|
92
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
93
|
-
maxBuffer: 64 * 1024 * 1024
|
|
94
|
-
});
|
|
95
|
-
if (result.error) {
|
|
96
|
-
const leaf = isNodeScript ? path.basename(args[0]) : path.basename(script);
|
|
97
|
-
const code = result.error.code ?? "ESPAWN";
|
|
98
|
-
const message = `leaf could not start: ${leaf} (${code})
|
|
99
|
-
Next: reinstall mm-harness (npm i -g @deeeed/metamask-harness) \u2014 the shell leaf is missing or not executable`;
|
|
100
|
-
process.stderr.write(`${message}
|
|
101
|
-
`);
|
|
102
|
-
return { status: 1, output: message };
|
|
103
|
-
}
|
|
104
|
-
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
105
|
-
recordCommandOutput(output);
|
|
106
|
-
if (!json && output) process.stderr.write(output);
|
|
107
|
-
return { status: result.status ?? 1, output };
|
|
108
|
-
}
|
|
109
|
-
function spawnInherit(script, args, cwd) {
|
|
110
|
-
const isNodeScript = script === process.execPath && args.length > 0;
|
|
111
|
-
const stem = (isNodeScript ? path.basename(args[0]) : path.basename(script)).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase();
|
|
112
|
-
const override = process.env[`MM_HARNESS_SCRIPT_BIN_${stem}`];
|
|
113
|
-
const bin = override ?? script;
|
|
114
|
-
const directArgs = override !== void 0 && isNodeScript ? args.slice(1) : args;
|
|
115
|
-
const { bin: invokeBin, args: spawnArgs } = resolveLeafInvoke(bin, directArgs);
|
|
116
|
-
const result = spawnSync(invokeBin, spawnArgs, { cwd, stdio: "inherit", env: process.env });
|
|
117
|
-
if (result.error) {
|
|
118
|
-
const leaf = isNodeScript ? path.basename(args[0]) : path.basename(script);
|
|
119
|
-
const code = result.error.code ?? "ESPAWN";
|
|
120
|
-
process.stderr.write(
|
|
121
|
-
`leaf could not start: ${leaf} (${code})
|
|
122
|
-
Next: reinstall mm-harness (npm i -g @deeeed/metamask-harness) \u2014 the leaf is missing or not executable
|
|
123
|
-
`
|
|
124
|
-
);
|
|
125
|
-
return 1;
|
|
126
|
-
}
|
|
127
|
-
if (result.status !== null) return result.status;
|
|
128
|
-
return result.signal === "SIGINT" || result.signal === "SIGTERM" ? 0 : 1;
|
|
129
|
-
}
|
|
130
|
-
function spawnScriptStreaming(script, args, cwd, options) {
|
|
131
|
-
const spawnOptions = options && ("env" in options || "timeoutMs" in options || "forward" in options || "capture" in options || "stdin" in options || "stage" in options) ? options : { env: options };
|
|
132
|
-
const isNodeScript = script === process.execPath && args.length > 0;
|
|
133
|
-
const stem = isNodeScript ? path.basename(args[0]).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase() : path.basename(script).replace(/[^A-Za-z0-9]/gu, "_").toUpperCase();
|
|
134
|
-
const override = process.env[`MM_HARNESS_SCRIPT_BIN_${stem}`];
|
|
135
|
-
const bin = override ?? script;
|
|
136
|
-
const directArgs = override !== void 0 && isNodeScript ? args.slice(1) : args;
|
|
137
|
-
if (shellLeafMissing(bin)) {
|
|
138
|
-
const message = `leaf could not start: ${path.basename(bin)} (ENOENT)
|
|
139
|
-
Next: reinstall mm-harness (npm i -g @deeeed/metamask-harness) \u2014 the shell leaf is missing or not executable`;
|
|
140
|
-
if (spawnOptions.forward !== "none") process.stderr.write(`${message}
|
|
141
|
-
`);
|
|
142
|
-
return Promise.resolve({ status: 1, output: message });
|
|
143
|
-
}
|
|
144
|
-
const { bin: invokeBin, args: spawnArgs } = resolveLeafInvoke(bin, directArgs);
|
|
145
|
-
recordCommandStage(spawnOptions.stage ?? path.basename(isNodeScript ? args[0] : script));
|
|
146
|
-
return new Promise((resolve, reject) => {
|
|
147
|
-
const ownsProcessGroup = process.platform !== "win32";
|
|
148
|
-
const child = spawn(invokeBin, spawnArgs, {
|
|
149
|
-
cwd,
|
|
150
|
-
env: spawnOptions.env ? { ...process.env, ...spawnOptions.env } : process.env,
|
|
151
|
-
stdio: [spawnOptions.stdin ?? "ignore", "pipe", "pipe"],
|
|
152
|
-
detached: ownsProcessGroup
|
|
153
|
-
});
|
|
154
|
-
let untrack;
|
|
155
|
-
try {
|
|
156
|
-
untrack = child.pid ? trackCheckoutChild(cwd, child.pid, ownsProcessGroup) : void 0;
|
|
157
|
-
} catch (error) {
|
|
158
|
-
child.once("error", reject);
|
|
159
|
-
child.once("close", () => reject(error));
|
|
160
|
-
try {
|
|
161
|
-
if (ownsProcessGroup && child.pid) process.kill(-child.pid, "SIGKILL");
|
|
162
|
-
else child.kill("SIGKILL");
|
|
163
|
-
} catch (killError) {
|
|
164
|
-
if (killError.code !== "ESRCH") reject(killError);
|
|
165
|
-
}
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const captured = new OperationOutputTail();
|
|
169
|
-
let settled = false;
|
|
170
|
-
let didTimeout = false;
|
|
171
|
-
let escalation;
|
|
172
|
-
let forwardedParentSignal;
|
|
173
|
-
let parentSignalFallback;
|
|
174
|
-
const parentSignals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
175
|
-
const removeParentSignalHandlers = () => {
|
|
176
|
-
for (const signal of parentSignals) process.removeListener(signal, parentSignalHandlers[signal]);
|
|
177
|
-
};
|
|
178
|
-
const finish = (result) => {
|
|
179
|
-
if (settled) return;
|
|
180
|
-
settled = true;
|
|
181
|
-
if (timer) clearTimeout(timer);
|
|
182
|
-
if (escalation) clearTimeout(escalation);
|
|
183
|
-
if (parentSignalFallback) clearTimeout(parentSignalFallback);
|
|
184
|
-
removeParentSignalHandlers();
|
|
185
|
-
resolve(result);
|
|
186
|
-
};
|
|
187
|
-
const signalChildTree = (signal) => {
|
|
188
|
-
try {
|
|
189
|
-
if (ownsProcessGroup && child.pid) process.kill(-child.pid, signal);
|
|
190
|
-
else child.kill(signal);
|
|
191
|
-
} catch {
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
const exitFromForwardedSignal = () => {
|
|
195
|
-
if (!forwardedParentSignal) return;
|
|
196
|
-
const signal = forwardedParentSignal;
|
|
197
|
-
forwardedParentSignal = void 0;
|
|
198
|
-
if (parentSignalFallback) clearTimeout(parentSignalFallback);
|
|
199
|
-
process.kill(process.pid, signal);
|
|
200
|
-
};
|
|
201
|
-
const parentSignalHandlers = Object.fromEntries(parentSignals.map((signal) => [signal, () => {
|
|
202
|
-
if (forwardedParentSignal) return;
|
|
203
|
-
forwardedParentSignal = signal;
|
|
204
|
-
signalChildTree(signal);
|
|
205
|
-
removeParentSignalHandlers();
|
|
206
|
-
if (Number(spawnOptions.timeoutMs) > 0) parentSignalFallback = setTimeout(() => {
|
|
207
|
-
signalChildTree("SIGKILL");
|
|
208
|
-
exitFromForwardedSignal();
|
|
209
|
-
}, 1e3);
|
|
210
|
-
}]));
|
|
211
|
-
if (ownsProcessGroup) {
|
|
212
|
-
for (const signal of parentSignals) process.once(signal, parentSignalHandlers[signal]);
|
|
213
|
-
}
|
|
214
|
-
const timeoutMs = spawnOptions.timeoutMs;
|
|
215
|
-
const timer = Number.isFinite(timeoutMs) && Number(timeoutMs) > 0 ? setTimeout(() => {
|
|
216
|
-
didTimeout = true;
|
|
217
|
-
const message = `leaf timed out after ${String(timeoutMs)}ms: ${path.basename(script)}`;
|
|
218
|
-
captured.append(`
|
|
219
|
-
${message}
|
|
220
|
-
`);
|
|
221
|
-
if (spawnOptions.forward !== "none") process.stderr.write(`${message}
|
|
222
|
-
`);
|
|
223
|
-
signalChildTree("SIGTERM");
|
|
224
|
-
escalation = setTimeout(() => {
|
|
225
|
-
signalChildTree("SIGKILL");
|
|
226
|
-
finish({ status: 1, output: captured.toString(), timedOut: true, timeoutMs: Number(timeoutMs) });
|
|
227
|
-
}, 1e3);
|
|
228
|
-
}, Number(timeoutMs)) : void 0;
|
|
229
|
-
const decoders = [new StringDecoder("utf8"), new StringDecoder("utf8")];
|
|
230
|
-
const tee = (chunk, stdout = false) => {
|
|
231
|
-
if (spawnOptions.capture !== false) captured.append(decoders[stdout ? 0 : 1].write(chunk));
|
|
232
|
-
recordCommandOutput(chunk);
|
|
233
|
-
if (spawnOptions.forward !== "none") {
|
|
234
|
-
(stdout && spawnOptions.forward === "original" ? process.stdout : process.stderr).write(chunk);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
child.stdout?.on("data", (chunk) => tee(chunk, true));
|
|
238
|
-
child.stderr?.on("data", tee);
|
|
239
|
-
child.on("error", (error) => {
|
|
240
|
-
const leaf = isNodeScript ? path.basename(args[0]) : path.basename(script);
|
|
241
|
-
const code = error.code ?? "ESPAWN";
|
|
242
|
-
const message = `leaf could not start: ${leaf} (${code})
|
|
243
|
-
Next: reinstall mm-harness (npm i -g @deeeed/metamask-harness) \u2014 the shell leaf is missing or not executable`;
|
|
244
|
-
if (spawnOptions.forward !== "none") process.stderr.write(`${message}
|
|
245
|
-
`);
|
|
246
|
-
finish({ status: 1, output: message, error });
|
|
247
|
-
});
|
|
248
|
-
child.on("exit", () => {
|
|
249
|
-
if (forwardedParentSignal) {
|
|
250
|
-
exitFromForwardedSignal();
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (!didTimeout) return;
|
|
254
|
-
signalChildTree("SIGKILL");
|
|
255
|
-
finish({ status: 1, output: captured.toString(), timedOut: true, timeoutMs: Number(timeoutMs) });
|
|
256
|
-
});
|
|
257
|
-
child.on("close", (status) => {
|
|
258
|
-
if (status !== null) untrack?.();
|
|
259
|
-
if (spawnOptions.capture !== false) captured.append(decoders.map((decoder) => decoder.end()).join(""));
|
|
260
|
-
if (didTimeout) signalChildTree("SIGKILL");
|
|
261
|
-
finish({
|
|
262
|
-
status: didTimeout ? 1 : status ?? 1,
|
|
263
|
-
output: captured.toString(),
|
|
264
|
-
...didTimeout ? { timedOut: true, timeoutMs: Number(timeoutMs) } : {}
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
import { ADAPTER_DETECT_NEXT } from "../harness.js";
|
|
270
|
-
function usageOut(json, command, message, userAction) {
|
|
271
|
-
if (json) {
|
|
272
|
-
console.log(
|
|
273
|
-
JSON.stringify(
|
|
274
|
-
{ schemaVersion: 1, command, status: "fail", exitCode: EXIT.usage, error: { code: "USAGE", message, userAction } },
|
|
275
|
-
null,
|
|
276
|
-
2
|
|
277
|
-
)
|
|
278
|
-
);
|
|
279
|
-
} else {
|
|
280
|
-
console.error(`\u2717 mm-harness ${command}: ${message}
|
|
281
|
-
Next: ${userAction}`);
|
|
282
|
-
}
|
|
283
|
-
return EXIT.usage;
|
|
284
|
-
}
|
|
285
|
-
function checkoutBusyOut(json, command, message, lockPath) {
|
|
286
|
-
const userAction = `wait for the current owner, or inspect ${lockPath} if its process has exited`;
|
|
287
|
-
if (json) {
|
|
288
|
-
console.log(
|
|
289
|
-
JSON.stringify(
|
|
290
|
-
{
|
|
291
|
-
schemaVersion: 1,
|
|
292
|
-
command,
|
|
293
|
-
status: "fail",
|
|
294
|
-
exitCode: EXIT.bounded,
|
|
295
|
-
recoverable: false,
|
|
296
|
-
error: { code: "SANDBOX_BUSY", message, userAction }
|
|
297
|
-
},
|
|
298
|
-
null,
|
|
299
|
-
2
|
|
300
|
-
)
|
|
301
|
-
);
|
|
302
|
-
} else {
|
|
303
|
-
console.error(`\u2717 mm-harness ${command}: ${message}
|
|
304
|
-
Next: ${userAction}`);
|
|
305
|
-
}
|
|
306
|
-
return EXIT.bounded;
|
|
307
|
-
}
|
|
308
|
-
export {
|
|
309
|
-
ADAPTER_DETECT_NEXT,
|
|
310
|
-
EXIT,
|
|
311
|
-
checkoutBusyOut,
|
|
312
|
-
flag,
|
|
313
|
-
parseFlags,
|
|
314
|
-
resolveAdapter,
|
|
315
|
-
scriptOverride,
|
|
316
|
-
spawnInherit,
|
|
317
|
-
spawnScript,
|
|
318
|
-
spawnScriptStreaming,
|
|
319
|
-
str,
|
|
320
|
-
targetOf,
|
|
321
|
-
usageOut,
|
|
322
|
-
writeInteractiveProgress
|
|
323
|
-
};
|
package/dist/commands/stop.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { color } from "../cli-color.js";
|
|
2
|
-
import { getAdapterSurface } from "../adapters/surface.js";
|
|
3
|
-
import { usageOut } from "./shared.js";
|
|
4
|
-
import { stopProjectCollector } from "../../library/actions/shared/analytics/collector.mjs";
|
|
5
|
-
import {
|
|
6
|
-
parseArgs,
|
|
7
|
-
optionFlag,
|
|
8
|
-
optionString,
|
|
9
|
-
resolveAdapter,
|
|
10
|
-
shellQuote
|
|
11
|
-
} from "./parse-args.js";
|
|
12
|
-
async function handleStop(argv) {
|
|
13
|
-
const { options } = parseArgs(argv, "stop");
|
|
14
|
-
const json = optionFlag(options, "json");
|
|
15
|
-
const { adapter, target } = resolveAdapter(options);
|
|
16
|
-
const surface = getAdapterSurface(adapter);
|
|
17
|
-
surface.resolveSlotPorts(target);
|
|
18
|
-
const explicitPort = optionString(options, "port") ?? optionString(options, "watcherPort");
|
|
19
|
-
if (explicitPort) {
|
|
20
|
-
process.env.WATCHER_PORT = explicitPort;
|
|
21
|
-
process.env.METRO_PORT = explicitPort;
|
|
22
|
-
}
|
|
23
|
-
const stop = surface.devServer.stop(target);
|
|
24
|
-
if (stop.kind === "headless") {
|
|
25
|
-
return usageOut(json, "stop", stop.message, stop.userAction);
|
|
26
|
-
}
|
|
27
|
-
const collector = await stopProjectCollector(target);
|
|
28
|
-
const signalled = (stop.signalled ?? 0) + (collector.stopped ? 1 : 0);
|
|
29
|
-
const summary = collector.stopped ? `${stop.summary}; stopped analytics collector ${collector.pid}` : stop.summary;
|
|
30
|
-
const next = surface.devServer.afterStop?.(target);
|
|
31
|
-
const userAction = `mm-harness status --target ${shellQuote(target)} --json`;
|
|
32
|
-
if (json) {
|
|
33
|
-
console.log(
|
|
34
|
-
JSON.stringify(
|
|
35
|
-
{
|
|
36
|
-
schemaVersion: 1,
|
|
37
|
-
command: "stop",
|
|
38
|
-
adapter,
|
|
39
|
-
target,
|
|
40
|
-
status: stop.status === 0 ? "pass" : "fail",
|
|
41
|
-
...stop.signalled !== void 0 || collector.stopped ? { signalled } : {},
|
|
42
|
-
exitCode: stop.status,
|
|
43
|
-
...stop.output ? { output: stop.output } : {},
|
|
44
|
-
...stop.status === 0 ? next ? { next } : {} : { error: { code: "DEV_SERVER_STOP_FAILED", message: summary, userAction } }
|
|
45
|
-
},
|
|
46
|
-
null,
|
|
47
|
-
2
|
|
48
|
-
)
|
|
49
|
-
);
|
|
50
|
-
} else {
|
|
51
|
-
if (stop.output) process.stderr.write(`${stop.output}
|
|
52
|
-
`);
|
|
53
|
-
console.error(`${color(stop.status === 0 ? "ok" : "err", stop.status === 0 ? "\u2713" : "\u2717")} ${summary}`);
|
|
54
|
-
if (stop.status !== 0 || next) console.error(` Next: ${stop.status === 0 ? next : userAction}`);
|
|
55
|
-
}
|
|
56
|
-
return stop.status;
|
|
57
|
-
}
|
|
58
|
-
export {
|
|
59
|
-
handleStop
|
|
60
|
-
};
|