@deeeed/metamask-harness 0.79.0 → 0.81.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 +73 -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 +16 -64
- 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/harness-source-fingerprint.mjs +13 -8
- package/adapters/shared/open-debug.mjs +2 -2
- 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/launch.mjs +17 -13
- package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
- package/adapters/terminal/lib/readiness.mjs +20 -12
- package/adapters/terminal/policy.mjs +35 -0
- package/adapters/terminal/verify.mjs +10 -3
- package/adapters/terminal/wallet-host.mjs +43 -225
- 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 +8 -4
- package/dist/adapters/core/overlay.js +1 -1
- package/dist/adapters/core/readiness.js +28 -1
- package/dist/adapters/core/run.js +2 -2
- package/dist/adapters/core/surface.js +16 -1
- package/dist/adapters/extension/actions.js +6 -2
- 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 +14 -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 +135 -14
- package/dist/adapters/extension/runtime-decision.js +2 -1
- package/dist/adapters/extension/runtime.js +9 -5
- package/dist/adapters/extension/surface.js +15 -2
- package/dist/adapters/extension/verify.js +2 -2
- package/dist/adapters/harness-freshness.js +2 -1
- package/dist/adapters/mobile/actions.js +4 -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/network-observer.js +68 -0
- package/dist/adapters/mobile/performance-observer.js +2 -4
- package/dist/adapters/mobile/prepare-target.js +1 -1
- package/dist/adapters/mobile/prepare.js +3 -4
- 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 +39 -15
- package/dist/adapters/mobile/runtime-decision.js +2 -4
- package/dist/adapters/mobile/source-freshness.js +22 -126
- package/dist/adapters/mobile/surface.js +26 -3
- 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/perps-order-inputs.js +36 -0
- package/dist/adapters/proof-failure.js +11 -0
- package/dist/adapters/slot-ports.js +1 -1
- package/dist/adapters/surface.js +1 -0
- package/dist/adapters/terminal/actions.js +4 -1
- package/dist/adapters/terminal/console-capture.js +1 -1
- package/dist/adapters/terminal/doctor-checks.js +2 -0
- package/dist/adapters/terminal/launch.js +4 -1
- package/dist/adapters/terminal/overlay.js +8 -1
- package/dist/adapters/terminal/run.js +33 -5
- package/dist/adapters/terminal/surface.js +9 -1
- package/dist/adapters.js +27 -7
- package/dist/advisor-policy.js +1 -1
- package/dist/checkout-view.js +1 -2
- package/dist/cli.js +59 -21
- package/dist/command-contract.js +1 -26
- package/dist/commands/check.js +33 -10
- package/dist/commands/checklist.js +1 -1
- package/dist/commands/completion-candidates.js +6 -14
- 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 +2 -8
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/commands/logs.js +3 -3
- 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 +4 -9
- 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/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 +5 -8
- 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/doctor.js +2 -3
- package/dist/engine.js +57 -0
- package/dist/funding-execution-context.js +96 -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 +4 -6
- package/dist/paths.js +34 -64
- package/dist/performance-observation.js +3 -5
- package/dist/progress.js +1 -1
- package/dist/recipe-assistance-context.js +6 -9
- package/dist/recipe-security.js +2 -59
- package/dist/review/knowledge.js +2 -2
- package/dist/runner.js +7 -4
- package/dist/runtime-context.js +1 -1
- package/dist/task-view.js +2 -3
- package/docs/CONTRIBUTING.md +5 -3
- package/docs/RECIPES.md +31 -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 -134
- package/library/actions/core/perps/_rewards.mjs +186 -0
- package/library/actions/core/perps/close_positions.mjs +1 -1
- package/library/actions/core/perps/place_order.mjs +167 -45
- package/library/actions/core/perps/quote_fees.mjs +61 -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/perps/close_visible_position.mjs +5 -0
- package/library/actions/extension/perps/edit_margin.mjs +4 -0
- package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
- package/library/actions/extension/perps/perps.mjs +320 -3
- package/library/actions/extension/perps/place_order.mjs +3 -1
- package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
- package/library/actions/extension/platform/cdp.mjs +73 -11
- package/library/actions/harness-exports.mjs +1 -6
- package/library/actions/mobile/app/network-control.mjs +6 -8
- package/library/actions/mobile/perps/perps.mjs +216 -11
- package/library/actions/mobile/perps/place_order.mjs +3 -1
- 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/shared/perps/order-policy.mjs +177 -0
- package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
- package/library/actions/terminal/app/launch.mjs +3 -2
- package/library/actions/terminal/platform/hud.mjs +2 -1
- package/library/actions/terminal/platform/page.mjs +2 -1
- package/library/actions/terminal/platform/runtime.mjs +2 -1
- package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
- package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
- package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
- package/library/manifests/core.action-manifest.json +87 -13
- package/library/manifests/extension.action-manifest.json +4 -0
- package/library/manifests/mobile.action-manifest.json +12 -0
- package/package.json +8 -9
- package/site/assets/help-recipes.json +1 -0
- 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/adapters/terminal/lib/origin.mjs +0 -30
- package/adapters/terminal/lib/page-script.mjs +0 -246
- package/adapters/terminal/lib/strict-wallet.mjs +0 -138
- 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/call.js +0 -685
- package/dist/commands/last.js +0 -60
- package/dist/commands/launch/index.js +0 -109
- package/dist/commands/list-executables.js +0 -247
- package/dist/commands/manifest.js +0 -488
- package/dist/commands/parse-args.js +0 -279
- package/dist/commands/reload.js +0 -41
- package/dist/commands/run-engine.js +0 -1340
- package/dist/commands/run-report.js +0 -241
- package/dist/commands/run.js +0 -751
- package/dist/commands/shared.js +0 -323
- package/dist/commands/stop.js +0 -60
- package/dist/devices.js +0 -100
- package/dist/discovery.js +0 -15
- 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/metamask-action-validation.js +0 -68
- package/dist/network-observation.js +0 -290
- 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
package/dist/commands/run.js
DELETED
|
@@ -1,751 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { importRecipeHarness, walletFixturePath } from "../paths.js";
|
|
4
|
-
import { color } from "../cli-color.js";
|
|
5
|
-
import { recipeRunning } from "../heal-bounds.js";
|
|
6
|
-
import { checkoutBusyOut, EXIT, usageOut, writeInteractiveProgress } from "./shared.js";
|
|
7
|
-
import {
|
|
8
|
-
optionFlag,
|
|
9
|
-
optionString,
|
|
10
|
-
resolveAdapter,
|
|
11
|
-
runtimeOptionsFromCli,
|
|
12
|
-
isRecord,
|
|
13
|
-
parseRecipeParamAssignments,
|
|
14
|
-
shellQuote,
|
|
15
|
-
targetPath,
|
|
16
|
-
usageError
|
|
17
|
-
} from "./parse-args.js";
|
|
18
|
-
import {
|
|
19
|
-
countRecipeNodes,
|
|
20
|
-
emitHealViolation,
|
|
21
|
-
executeWithHealBounds,
|
|
22
|
-
preflightRecipe,
|
|
23
|
-
prepareHeal,
|
|
24
|
-
persistRunEffects,
|
|
25
|
-
runRecipe,
|
|
26
|
-
validateRunRecipeStatic
|
|
27
|
-
} from "./run-engine.js";
|
|
28
|
-
import { handleDescribeRecipe, handleListExecutables } from "./list-executables.js";
|
|
29
|
-
import { applyDeviceTargeting } from "./device-target.js";
|
|
30
|
-
import { getAdapterSurface } from "../adapters/surface.js";
|
|
31
|
-
import {
|
|
32
|
-
indexProductProvenanceArtifact,
|
|
33
|
-
writeRunReport
|
|
34
|
-
} from "./run-report.js";
|
|
35
|
-
import { acquireCheckoutLock } from "../checkout-lock.js";
|
|
36
|
-
import { JsonStreamWriter } from "../json-stream.js";
|
|
37
|
-
import { formatRunDiagnosticsForHuman, readRunDiagnosticsDocument } from "../run-diagnostics.js";
|
|
38
|
-
import { recipeTrustFailure } from "../recipe-security.js";
|
|
39
|
-
import { ProvenanceDriftError } from "../execution-provenance.js";
|
|
40
|
-
import { recordCommandEvidence } from "../command-journal.js";
|
|
41
|
-
import { actionLibraryContextArgs, resolveActionCapabilityMatrix } from "./manifest.js";
|
|
42
|
-
import { missingActionCapabilities } from "@farmslot/recipe-cli";
|
|
43
|
-
import {
|
|
44
|
-
startRunNetworkObservation
|
|
45
|
-
} from "../network-observation.js";
|
|
46
|
-
import {
|
|
47
|
-
startRunPerformanceObservation
|
|
48
|
-
} from "../performance-observation.js";
|
|
49
|
-
async function validationCapabilityRefusals(adapter, findings, librarySources) {
|
|
50
|
-
const actionNames = findings.flatMap((finding) => {
|
|
51
|
-
if (finding.code !== "recipe.action_not_declared_by_manifest") return [];
|
|
52
|
-
const match = /^Recipe action ([^ ]+) is not declared by the runner action manifest[.]?$/u.exec(finding.message);
|
|
53
|
-
return match?.[1] ? [match[1]] : [];
|
|
54
|
-
});
|
|
55
|
-
if (actionNames.length === 0) return [];
|
|
56
|
-
return missingActionCapabilities(
|
|
57
|
-
adapter,
|
|
58
|
-
actionNames,
|
|
59
|
-
await resolveActionCapabilityMatrix(librarySources)
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
function capabilityValidationUserAction(adapter, refusals, libraryContextArgs) {
|
|
63
|
-
const capabilities = refusals.map(
|
|
64
|
-
(refusal) => `"${refusal.capability}" [${refusal.satisfyingAdapters.join(", ")}]`
|
|
65
|
-
).join("; ");
|
|
66
|
-
return `Missing action capabilities for ${adapter}, with satisfying adapters: ${capabilities}. Inspect: mm-harness actions --matrix${libraryContextArgs} --json; then rerun from a checkout matching a satisfying adapter, splitting the recipe if no single adapter satisfies every capability`;
|
|
67
|
-
}
|
|
68
|
-
async function handleRun(parsed) {
|
|
69
|
-
const stream = new JsonStreamWriter("run", optionFlag(parsed.options, "jsonStream"));
|
|
70
|
-
const restoreStdout = stream.isolateStdout();
|
|
71
|
-
const target = targetPath(parsed.options);
|
|
72
|
-
try {
|
|
73
|
-
const exitCode = await handleRunInner(parsed, stream);
|
|
74
|
-
stream.complete(exitCode === EXIT.ok ? "pass" : "fail", exitCode);
|
|
75
|
-
return exitCode;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
if (error instanceof ProvenanceDriftError) {
|
|
78
|
-
const failure = {
|
|
79
|
-
code: error.code,
|
|
80
|
-
message: error.message,
|
|
81
|
-
userAction: error.userAction,
|
|
82
|
-
provenancePath: error.provenancePath,
|
|
83
|
-
drift: error.drift
|
|
84
|
-
};
|
|
85
|
-
if (stream.enabled) {
|
|
86
|
-
stream.error(failure);
|
|
87
|
-
stream.complete("fail", error.exitCode);
|
|
88
|
-
} else if (optionFlag(parsed.options, "json")) {
|
|
89
|
-
console.log(JSON.stringify({
|
|
90
|
-
schemaVersion: 1,
|
|
91
|
-
command: "run",
|
|
92
|
-
status: "fail",
|
|
93
|
-
error: failure,
|
|
94
|
-
exitCode: error.exitCode
|
|
95
|
-
}, null, 2));
|
|
96
|
-
} else {
|
|
97
|
-
console.error(`\u2717 mm-harness run: ${error.message}`);
|
|
98
|
-
console.error(` Next: ${error.userAction}`);
|
|
99
|
-
}
|
|
100
|
-
return error.exitCode;
|
|
101
|
-
}
|
|
102
|
-
const trustFailure = recipeTrustFailure(error);
|
|
103
|
-
if (trustFailure) {
|
|
104
|
-
if (stream.enabled) {
|
|
105
|
-
stream.error(trustFailure);
|
|
106
|
-
stream.complete("fail", EXIT.validation);
|
|
107
|
-
} else if (optionFlag(parsed.options, "json")) {
|
|
108
|
-
console.log(JSON.stringify({
|
|
109
|
-
schemaVersion: 1,
|
|
110
|
-
command: "run",
|
|
111
|
-
status: "fail",
|
|
112
|
-
error: trustFailure,
|
|
113
|
-
exitCode: EXIT.validation
|
|
114
|
-
}, null, 2));
|
|
115
|
-
} else {
|
|
116
|
-
console.error(`\u2717 mm-harness run: ${trustFailure.message}`);
|
|
117
|
-
const blocked = trustFailure.details?.blocked;
|
|
118
|
-
if (blocked?.length) {
|
|
119
|
-
console.error(" Restricted plan nodes:");
|
|
120
|
-
for (const node of blocked.slice(0, 10)) {
|
|
121
|
-
const implementation = node.implementation?.digest ? ` implementation=${node.implementation.kind ?? "custom"}@${node.implementation.digest}` : "";
|
|
122
|
-
console.error(
|
|
123
|
-
` - ${node.nodeId}: ${node.action} [${node.capabilities.join(", ")}] source=${node.source}${implementation}`
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
if (blocked.length > 10) console.error(` - \u2026 ${blocked.length - 10} more (use --json)`);
|
|
127
|
-
}
|
|
128
|
-
console.error(` Next: ${trustFailure.userAction}`);
|
|
129
|
-
}
|
|
130
|
-
return EXIT.validation;
|
|
131
|
-
}
|
|
132
|
-
const exitCode = error !== null && typeof error === "object" && "exitCode" in error && typeof error.exitCode === "number" ? error.exitCode : EXIT.runtime;
|
|
133
|
-
stream.error({
|
|
134
|
-
code: exitCode === EXIT.usage ? "CLI_USAGE_ERROR" : "RUN_FAILED",
|
|
135
|
-
message: error instanceof Error ? error.message : String(error),
|
|
136
|
-
userAction: `mm-harness doctor --target ${shellQuote(target)} --json`
|
|
137
|
-
});
|
|
138
|
-
stream.complete("fail", exitCode);
|
|
139
|
-
throw error;
|
|
140
|
-
} finally {
|
|
141
|
-
restoreStdout();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
async function handleRunInner({ positional, options }, stream) {
|
|
145
|
-
if (!optionFlag(options, "list") && ["domain", "source", "sort"].some((name) => optionString(options, name) !== void 0)) {
|
|
146
|
-
throw usageError("--domain, --source, and --sort require run --list.");
|
|
147
|
-
}
|
|
148
|
-
if (optionFlag(options, "describe") && (optionFlag(options, "list") || optionFlag(options, "plan"))) {
|
|
149
|
-
const message = "--describe cannot be combined with --list or --plan.";
|
|
150
|
-
const userAction = "choose one: mm-harness run --list, mm-harness run <recipe> --describe, or mm-harness run <recipe> --plan";
|
|
151
|
-
if (stream.enabled) {
|
|
152
|
-
stream.error({ code: "CLI_USAGE_ERROR", message, userAction });
|
|
153
|
-
return EXIT.usage;
|
|
154
|
-
}
|
|
155
|
-
return usageOut(optionFlag(options, "json"), "run", message, userAction);
|
|
156
|
-
}
|
|
157
|
-
if (optionFlag(options, "list")) {
|
|
158
|
-
if (stream.enabled) {
|
|
159
|
-
const message = "--json-stream is for recipe execution; use run --list --json for discovery.";
|
|
160
|
-
stream.error({ code: "JSON_STREAM_UNSUPPORTED_MODE", message, userAction: "mm-harness run --list --json" });
|
|
161
|
-
return EXIT.usage;
|
|
162
|
-
}
|
|
163
|
-
return handleListExecutables("run", options);
|
|
164
|
-
}
|
|
165
|
-
const targetRecipe = positional[0];
|
|
166
|
-
if (!targetRecipe) throw usageError("run requires <recipe.json>.");
|
|
167
|
-
const paramAssignments = positional.slice(1);
|
|
168
|
-
if (optionFlag(options, "describe")) {
|
|
169
|
-
if (paramAssignments.length > 0) {
|
|
170
|
-
throw usageError("run --describe does not accept parameter values; inspect the declaration first.");
|
|
171
|
-
}
|
|
172
|
-
if (stream.enabled) {
|
|
173
|
-
const message = "--json-stream is for recipe execution; use --describe --json for discovery.";
|
|
174
|
-
stream.error({
|
|
175
|
-
code: "JSON_STREAM_UNSUPPORTED_MODE",
|
|
176
|
-
message,
|
|
177
|
-
userAction: `mm-harness run ${shellQuote(targetRecipe)} --describe --json`
|
|
178
|
-
});
|
|
179
|
-
return EXIT.usage;
|
|
180
|
-
}
|
|
181
|
-
return handleDescribeRecipe(targetRecipe, options);
|
|
182
|
-
}
|
|
183
|
-
const params = parseRecipeParamAssignments(paramAssignments);
|
|
184
|
-
if (optionFlag(options, "plan")) return handleRunPlan(targetRecipe, params, options, stream);
|
|
185
|
-
const { adapter, target } = resolveAdapter(options);
|
|
186
|
-
const json = optionFlag(options, "json");
|
|
187
|
-
const jsonOutput = json && !stream.enabled;
|
|
188
|
-
const machine = json || stream.enabled;
|
|
189
|
-
stream.phase("resolve", { adapter, target, recipe: targetRecipe });
|
|
190
|
-
if (!fs.existsSync(target)) {
|
|
191
|
-
const message = `target does not exist: ${target}`;
|
|
192
|
-
const userAction = "pass --target <metamask-checkout> pointing to an existing checkout";
|
|
193
|
-
if (stream.enabled) {
|
|
194
|
-
stream.error({ code: "TARGET_NOT_FOUND", message, userAction });
|
|
195
|
-
return EXIT.usage;
|
|
196
|
-
}
|
|
197
|
-
return usageOut(jsonOutput, "run", message, userAction);
|
|
198
|
-
}
|
|
199
|
-
writeInteractiveProgress(machine, `\u2192 recipe run \u2014 validating ${targetRecipe} \xB7 ${adapter}`);
|
|
200
|
-
getAdapterSurface(adapter).resolveSlotPorts(target);
|
|
201
|
-
const dtResult = applyDeviceTargeting("run", adapter, options, { gate: true, rerun: "" });
|
|
202
|
-
if ("code" in dtResult) {
|
|
203
|
-
return emitRunUsageError(jsonOutput, stream, adapter, targetRecipe, dtResult.code, dtResult.message, dtResult.userAction);
|
|
204
|
-
}
|
|
205
|
-
if (recipeRunning(target)) return emitRunRecipeRunning(jsonOutput, stream, target);
|
|
206
|
-
stream.phase("validate");
|
|
207
|
-
const validated = await validateRunRecipeStatic(targetRecipe, adapter, options, params);
|
|
208
|
-
if (validated.usageError) {
|
|
209
|
-
const userAction = runUsageRecovery(
|
|
210
|
-
validated.usageError.code,
|
|
211
|
-
validated.usageError.message,
|
|
212
|
-
adapter,
|
|
213
|
-
validated.recipeFile
|
|
214
|
-
);
|
|
215
|
-
return emitRunUsageError(
|
|
216
|
-
jsonOutput,
|
|
217
|
-
stream,
|
|
218
|
-
adapter,
|
|
219
|
-
validated.recipeFile,
|
|
220
|
-
validated.usageError.code,
|
|
221
|
-
validated.usageError.message,
|
|
222
|
-
userAction
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
if (validated.errorCount > 0) {
|
|
226
|
-
const capabilityRefusals = optionString(options, "actionManifest") ? [] : await validationCapabilityRefusals(
|
|
227
|
-
adapter,
|
|
228
|
-
validated.findings,
|
|
229
|
-
validated.librarySources
|
|
230
|
-
);
|
|
231
|
-
return emitRunValidationError(
|
|
232
|
-
jsonOutput,
|
|
233
|
-
stream,
|
|
234
|
-
adapter,
|
|
235
|
-
validated.recipeFile,
|
|
236
|
-
validated.findings,
|
|
237
|
-
validated.errorCount,
|
|
238
|
-
capabilityRefusals,
|
|
239
|
-
actionLibraryContextArgs(validated.librarySources)
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
const depsBlock = await getAdapterSurface(adapter).run?.dependencyBlock?.(target, {
|
|
243
|
-
recipe: validated.recipe,
|
|
244
|
-
librarySources: validated.librarySources
|
|
245
|
-
}) ?? null;
|
|
246
|
-
if (depsBlock) {
|
|
247
|
-
return emitRunUsageError(jsonOutput, stream, adapter, validated.recipeFile, depsBlock.code, depsBlock.message, depsBlock.userAction);
|
|
248
|
-
}
|
|
249
|
-
let artifactsDir;
|
|
250
|
-
try {
|
|
251
|
-
artifactsDir = resolveRunArtifactsDir(target, optionString(options, "artifactsDir"));
|
|
252
|
-
} catch (error) {
|
|
253
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
254
|
-
return emitRunUsageError(
|
|
255
|
-
jsonOutput,
|
|
256
|
-
stream,
|
|
257
|
-
adapter,
|
|
258
|
-
validated.recipeFile,
|
|
259
|
-
"ARTIFACT_DIR_INVALID",
|
|
260
|
-
message,
|
|
261
|
-
"set RECIPE_TASK_DIR/FARMSLOT_TASK_DIR inside the checkout or pass --artifacts-dir <path>"
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
recordCommandEvidence(artifactsDir);
|
|
265
|
-
const librarySources = validated.librarySources;
|
|
266
|
-
let networkObservation;
|
|
267
|
-
let performanceObservation;
|
|
268
|
-
const runtimeOptions = {
|
|
269
|
-
...runtimeOptionsFromCli(options),
|
|
270
|
-
params,
|
|
271
|
-
...librarySources ? { librarySources } : {},
|
|
272
|
-
stdoutIsMachineContract: machine,
|
|
273
|
-
onActionEvent: ({ nodeId, action, status }) => {
|
|
274
|
-
stream.node(nodeId, action, status);
|
|
275
|
-
networkObservation?.onActionEvent({ nodeId, action, status });
|
|
276
|
-
performanceObservation?.onActionEvent({ nodeId, action, status });
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
stream.phase("authorize");
|
|
280
|
-
let preflightedExecution = await preflightRecipe(
|
|
281
|
-
adapter,
|
|
282
|
-
validated.recipeFile,
|
|
283
|
-
artifactsDir,
|
|
284
|
-
target,
|
|
285
|
-
optionString(options, "actionManifest"),
|
|
286
|
-
runtimeOptions
|
|
287
|
-
);
|
|
288
|
-
const lock = acquireCheckoutLock(target, "run");
|
|
289
|
-
if ("message" in lock) {
|
|
290
|
-
const userAction = `wait for the current owner, or inspect ${lock.path} if its process has exited`;
|
|
291
|
-
stream.error({ code: "SANDBOX_BUSY", message: lock.message, userAction });
|
|
292
|
-
return checkoutBusyOut(jsonOutput, "run", lock.message, lock.path);
|
|
293
|
-
}
|
|
294
|
-
try {
|
|
295
|
-
const prepared = await prepareHeal(adapter, target, options, machine, {
|
|
296
|
-
onPhase: (phase, fields) => stream.phase(phase, fields)
|
|
297
|
-
});
|
|
298
|
-
if (typeof prepared === "number") {
|
|
299
|
-
stream.error({
|
|
300
|
-
code: "RUN_PREPARE_FAILED",
|
|
301
|
-
message: "runtime preparation failed; inspect stderr for the exact probe",
|
|
302
|
-
userAction: `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`
|
|
303
|
-
});
|
|
304
|
-
return prepared;
|
|
305
|
-
}
|
|
306
|
-
const { state } = prepared;
|
|
307
|
-
preflightedExecution = await preflightRecipe(
|
|
308
|
-
adapter,
|
|
309
|
-
validated.recipeFile,
|
|
310
|
-
artifactsDir,
|
|
311
|
-
target,
|
|
312
|
-
optionString(options, "actionManifest"),
|
|
313
|
-
runtimeOptions
|
|
314
|
-
);
|
|
315
|
-
networkObservation = await startRunNetworkObservation(
|
|
316
|
-
adapter,
|
|
317
|
-
target,
|
|
318
|
-
artifactsDir,
|
|
319
|
-
process.env,
|
|
320
|
-
{
|
|
321
|
-
cdpPort: runtimeOptions.cdpPort,
|
|
322
|
-
watcherPort: runtimeOptions.watcherPort
|
|
323
|
-
}
|
|
324
|
-
);
|
|
325
|
-
performanceObservation = startRunPerformanceObservation(
|
|
326
|
-
adapter,
|
|
327
|
-
target,
|
|
328
|
-
artifactsDir,
|
|
329
|
-
process.env,
|
|
330
|
-
{
|
|
331
|
-
cdpPort: runtimeOptions.cdpPort,
|
|
332
|
-
watcherPort: runtimeOptions.watcherPort
|
|
333
|
-
}
|
|
334
|
-
);
|
|
335
|
-
stream.phase("execute");
|
|
336
|
-
let executionResult;
|
|
337
|
-
try {
|
|
338
|
-
executionResult = await executeWithHealBounds(
|
|
339
|
-
// validated.recipeFile, not the raw arg: the arg may be a library recipe NAME
|
|
340
|
-
// that only the resolver knows how to turn into a file.
|
|
341
|
-
() => {
|
|
342
|
-
const execution = preflightedExecution;
|
|
343
|
-
preflightedExecution = void 0;
|
|
344
|
-
return runRecipe(
|
|
345
|
-
adapter,
|
|
346
|
-
validated.recipeFile,
|
|
347
|
-
artifactsDir,
|
|
348
|
-
target,
|
|
349
|
-
optionString(options, "actionManifest"),
|
|
350
|
-
runtimeOptions,
|
|
351
|
-
execution,
|
|
352
|
-
state
|
|
353
|
-
);
|
|
354
|
-
},
|
|
355
|
-
target,
|
|
356
|
-
state
|
|
357
|
-
);
|
|
358
|
-
} catch (error) {
|
|
359
|
-
await networkObservation?.finalize().catch(() => void 0);
|
|
360
|
-
networkObservation = void 0;
|
|
361
|
-
await performanceObservation?.finalize().catch(() => void 0);
|
|
362
|
-
performanceObservation = void 0;
|
|
363
|
-
throw error;
|
|
364
|
-
}
|
|
365
|
-
const { result, violation } = executionResult;
|
|
366
|
-
await networkObservation?.finalize(result.artifactManifestPath);
|
|
367
|
-
networkObservation = void 0;
|
|
368
|
-
await performanceObservation?.finalize(result.artifactManifestPath);
|
|
369
|
-
performanceObservation = void 0;
|
|
370
|
-
for (const mutation of state.mutations) stream.mutation(mutation);
|
|
371
|
-
for (const recovery of state.recovered) stream.recovery(recovery);
|
|
372
|
-
persistRunEffects(result.summaryPath, result.artifactManifestPath, state);
|
|
373
|
-
indexProductProvenanceArtifact(
|
|
374
|
-
result.artifactManifestPath,
|
|
375
|
-
target,
|
|
376
|
-
adapter,
|
|
377
|
-
result.browser ?? null
|
|
378
|
-
);
|
|
379
|
-
if (violation !== null) {
|
|
380
|
-
const userAction = violation.userAction ?? `inspect ${shellQuote(result.summaryPath)} and ${shellQuote(result.tracePath)}; fix the application or recipe failure before retrying`;
|
|
381
|
-
stream.error({
|
|
382
|
-
code: violation.code,
|
|
383
|
-
message: violation.message,
|
|
384
|
-
userAction,
|
|
385
|
-
originalError: violation.originalError ?? null
|
|
386
|
-
});
|
|
387
|
-
return emitHealViolation(jsonOutput, "run", result, violation, state, adapter);
|
|
388
|
-
}
|
|
389
|
-
const report = writeRunReport(result);
|
|
390
|
-
const exitCode = result.status === "pass" ? EXIT.ok : EXIT.runtime;
|
|
391
|
-
const failureUserAction = `mm-harness last --target ${shellQuote(target)} --json`;
|
|
392
|
-
if (stream.enabled) {
|
|
393
|
-
if (result.status === "fail") {
|
|
394
|
-
stream.error({
|
|
395
|
-
code: "RECIPE_EXECUTION_FAILED",
|
|
396
|
-
message: "recipe execution failed; inspect the persisted result and evidence paths",
|
|
397
|
-
userAction: failureUserAction
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
stream.complete(result.status, exitCode, {
|
|
401
|
-
adapter,
|
|
402
|
-
reportPath: report.path,
|
|
403
|
-
summaryPath: result.summaryPath,
|
|
404
|
-
tracePath: result.tracePath,
|
|
405
|
-
artifactManifestPath: result.artifactManifestPath,
|
|
406
|
-
recovered: state.recovered,
|
|
407
|
-
mutations: state.mutations
|
|
408
|
-
});
|
|
409
|
-
} else if (json) {
|
|
410
|
-
console.log(
|
|
411
|
-
JSON.stringify(
|
|
412
|
-
{
|
|
413
|
-
schemaVersion: 1,
|
|
414
|
-
command: "run",
|
|
415
|
-
adapter,
|
|
416
|
-
status: result.status,
|
|
417
|
-
exitCode,
|
|
418
|
-
recovered: state.recovered,
|
|
419
|
-
mutations: state.mutations,
|
|
420
|
-
reportPath: report.path,
|
|
421
|
-
result,
|
|
422
|
-
...result.status === "fail" ? { error: { code: "RECIPE_EXECUTION_FAILED", message: "recipe execution failed; inspect the persisted result and evidence paths", userAction: failureUserAction } } : {}
|
|
423
|
-
},
|
|
424
|
-
null,
|
|
425
|
-
2
|
|
426
|
-
)
|
|
427
|
-
);
|
|
428
|
-
} else {
|
|
429
|
-
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
430
|
-
console.log(`${out(result.status === "pass" ? "ok" : "err", result.status.toUpperCase())} ${out("bold", "recipe run")} ${out("dim", `[${adapter}]`)}`);
|
|
431
|
-
if (report.preview.length > 0) {
|
|
432
|
-
console.log(out("label", "summary:"));
|
|
433
|
-
for (const line of report.preview) console.log(` ${formatPreviewLine(line, out)}`);
|
|
434
|
-
}
|
|
435
|
-
const artifacts = runArtifactInventory(result.artifactManifestPath);
|
|
436
|
-
console.log(out("label", `artifacts (${artifacts.length}):`));
|
|
437
|
-
for (const artifact of artifacts) {
|
|
438
|
-
console.log(` ${out("dim", `${artifact.label}:`)} ${out("path", artifact.absolutePath)}`);
|
|
439
|
-
}
|
|
440
|
-
if (result.status === "fail") {
|
|
441
|
-
console.error(` Next: ${failureUserAction}`);
|
|
442
|
-
}
|
|
443
|
-
console.log(out("label", "diagnostics:"));
|
|
444
|
-
const diagnostics = readRunDiagnosticsDocument(result.diagnosticsPath);
|
|
445
|
-
for (const line of formatRunDiagnosticsForHuman(diagnostics, adapter)) {
|
|
446
|
-
console.log(` ${formatDiagnosticLine(line, out)}`);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return exitCode;
|
|
450
|
-
} finally {
|
|
451
|
-
lock.release();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
function formatDiagnosticLine(line, out) {
|
|
455
|
-
const match = /^(CLEAN|REVIEW|UNAVAILABLE|N\/A|WARNING|ERROR|EXCEPTION)(.*)$/u.exec(line);
|
|
456
|
-
if (!match) return line;
|
|
457
|
-
const status = match[1];
|
|
458
|
-
const style = status === "CLEAN" ? "ok" : status === "ERROR" || status === "EXCEPTION" ? "err" : "warn";
|
|
459
|
-
return `${out(style, status)}${match[2]}`;
|
|
460
|
-
}
|
|
461
|
-
function resolveRunArtifactsDir(target, explicit) {
|
|
462
|
-
if (explicit !== void 0) return path.resolve(explicit);
|
|
463
|
-
const taskDir = process.env.RECIPE_TASK_DIR || process.env.FARMSLOT_TASK_DIR;
|
|
464
|
-
if (taskDir) {
|
|
465
|
-
const resolvedTask = path.resolve(target, taskDir);
|
|
466
|
-
const relative = path.relative(path.resolve(target), resolvedTask);
|
|
467
|
-
if (!relative.startsWith(`..${path.sep}`) && relative !== "..") {
|
|
468
|
-
return path.join(resolvedTask, "artifacts");
|
|
469
|
-
}
|
|
470
|
-
throw new Error(`task directory must be inside the target checkout: ${taskDir}`);
|
|
471
|
-
}
|
|
472
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/gu, "");
|
|
473
|
-
return path.join(target, "temp", "recipe", "runs", `${stamp}-${process.pid}`);
|
|
474
|
-
}
|
|
475
|
-
function runArtifactInventory(manifestPathValue) {
|
|
476
|
-
const manifestPath = path.resolve(String(manifestPathValue));
|
|
477
|
-
const root = path.dirname(manifestPath);
|
|
478
|
-
let manifest;
|
|
479
|
-
try {
|
|
480
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
481
|
-
} catch {
|
|
482
|
-
return [{ absolutePath: manifestPath, basename: path.basename(manifestPath), label: "Artifact manifest" }];
|
|
483
|
-
}
|
|
484
|
-
const entries = isRecord(manifest) && Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
|
|
485
|
-
const artifacts = [];
|
|
486
|
-
for (const entry of entries) {
|
|
487
|
-
if (!isRecord(entry) || typeof entry.path !== "string" || entry.path.length === 0) continue;
|
|
488
|
-
const absolutePath = path.resolve(root, entry.path);
|
|
489
|
-
const basename = path.basename(absolutePath);
|
|
490
|
-
const label = typeof entry.label === "string" && entry.label.length > 0 ? entry.label : basename;
|
|
491
|
-
artifacts.push({ absolutePath, basename, label });
|
|
492
|
-
}
|
|
493
|
-
artifacts.push({ absolutePath: manifestPath, basename: path.basename(manifestPath), label: "Artifact manifest" });
|
|
494
|
-
const priority = /* @__PURE__ */ new Map([
|
|
495
|
-
["diagnostics.json", 0],
|
|
496
|
-
["report.md", 1],
|
|
497
|
-
["summary.json", 2],
|
|
498
|
-
["trace.json", 3],
|
|
499
|
-
["recipe.json", 4],
|
|
500
|
-
["artifact-manifest.json", 100]
|
|
501
|
-
]);
|
|
502
|
-
return artifacts.filter((artifact, index, all) => all.findIndex((candidate) => candidate.absolutePath === artifact.absolutePath) === index).sort((left, right) => (priority.get(left.basename) ?? 50) - (priority.get(right.basename) ?? 50));
|
|
503
|
-
}
|
|
504
|
-
function formatPreviewLine(line, out) {
|
|
505
|
-
const match = /^(PASS|FAIL)\s+(.+)$/u.exec(line);
|
|
506
|
-
if (!match) return line;
|
|
507
|
-
const status = match[1];
|
|
508
|
-
const rest = match[2];
|
|
509
|
-
return `${out(status === "PASS" ? "ok" : "err", status)} ${rest}`;
|
|
510
|
-
}
|
|
511
|
-
async function handleRunPlan(recipeArg, params, options, stream) {
|
|
512
|
-
const json = optionFlag(options, "json");
|
|
513
|
-
const jsonOutput = json && !stream.enabled;
|
|
514
|
-
const { adapter, target } = resolveAdapter(options);
|
|
515
|
-
stream.phase("resolve", { adapter, target, recipe: recipeArg });
|
|
516
|
-
stream.phase("validate");
|
|
517
|
-
const validated = await validateRunRecipeStatic(recipeArg, adapter, options, params);
|
|
518
|
-
if (validated.usageError) {
|
|
519
|
-
const userAction = runUsageRecovery(
|
|
520
|
-
validated.usageError.code,
|
|
521
|
-
validated.usageError.message,
|
|
522
|
-
adapter,
|
|
523
|
-
validated.recipeFile
|
|
524
|
-
);
|
|
525
|
-
return emitPlanUsageError(
|
|
526
|
-
jsonOutput,
|
|
527
|
-
stream,
|
|
528
|
-
adapter,
|
|
529
|
-
validated.recipeFile,
|
|
530
|
-
validated.usageError.code,
|
|
531
|
-
validated.usageError.message,
|
|
532
|
-
userAction
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
const {
|
|
536
|
-
recipe,
|
|
537
|
-
recipeFile,
|
|
538
|
-
findings,
|
|
539
|
-
errorCount,
|
|
540
|
-
manifestOk,
|
|
541
|
-
schemaValid,
|
|
542
|
-
effectiveParams,
|
|
543
|
-
librarySources
|
|
544
|
-
} = validated;
|
|
545
|
-
const status = errorCount === 0 ? "pass" : "fail";
|
|
546
|
-
const capabilityRefusals = status === "fail" && !optionString(options, "actionManifest") ? await validationCapabilityRefusals(adapter, findings, librarySources) : [];
|
|
547
|
-
const failureUserAction = capabilityRefusals.length > 0 ? capabilityValidationUserAction(
|
|
548
|
-
adapter,
|
|
549
|
-
capabilityRefusals,
|
|
550
|
-
actionLibraryContextArgs(librarySources)
|
|
551
|
-
) : runPlanProbe(adapter, recipeFile);
|
|
552
|
-
const nodeCount = countRecipeNodes(recipe);
|
|
553
|
-
const artifactsDir = optionString(options, "artifactsDir");
|
|
554
|
-
const plan = [
|
|
555
|
-
{ step: "resolve.recipe", confidence: "static", status: "ok", detail: recipeFile },
|
|
556
|
-
{ step: "resolve.adapter", confidence: "static", status: "ok", detail: adapter },
|
|
557
|
-
{
|
|
558
|
-
step: "resolve.artifactsDir",
|
|
559
|
-
confidence: "static",
|
|
560
|
-
status: "ok",
|
|
561
|
-
detail: artifactsDir ? path.resolve(artifactsDir) : "(resolved to the slot artifacts dir at run time)"
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
step: "validate.manifest",
|
|
565
|
-
confidence: "static",
|
|
566
|
-
status: manifestOk ? "ok" : "error",
|
|
567
|
-
detail: manifestOk ? "action manifest is well-formed" : "action manifest failed validation"
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
step: "validate.schema",
|
|
571
|
-
confidence: "static",
|
|
572
|
-
status: manifestOk && schemaValid ? "ok" : "error",
|
|
573
|
-
detail: "recipe document schema + action existence/platform vs the adapter manifest"
|
|
574
|
-
},
|
|
575
|
-
...optionFlag(options, "proof") ? [{
|
|
576
|
-
step: "validate.proofBindings",
|
|
577
|
-
confidence: "static",
|
|
578
|
-
status: findings.some((finding) => finding.code.startsWith("proof.")) ? "error" : errorCount ? "planned" : "ok",
|
|
579
|
-
detail: "checks possible behavioral assertion bindings only; runtime order, branches, results and proof artifact indexing remain unverified"
|
|
580
|
-
}] : [],
|
|
581
|
-
{
|
|
582
|
-
step: "fixture.file",
|
|
583
|
-
confidence: "static",
|
|
584
|
-
status: "ok",
|
|
585
|
-
detail: fs.existsSync(walletFixturePath(target)) ? `present: ${walletFixturePath(target)}` : `absent: ${walletFixturePath(target)} (fixtures set to seed)`
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
step: "overlay.ensure",
|
|
589
|
-
confidence: "conditional",
|
|
590
|
-
status: "planned",
|
|
591
|
-
detail: "would auto-ensure the runtime overlay if missing (install phase)"
|
|
592
|
-
},
|
|
593
|
-
...getAdapterSurface(adapter).headless ? [] : [
|
|
594
|
-
{
|
|
595
|
-
step: "launch.app",
|
|
596
|
-
confidence: "conditional",
|
|
597
|
-
status: "planned",
|
|
598
|
-
detail: "would launch/attach the app + heal transport (Metro/Chrome/CDP) before executing"
|
|
599
|
-
}
|
|
600
|
-
],
|
|
601
|
-
{
|
|
602
|
-
step: "execute.nodes",
|
|
603
|
-
confidence: "conditional",
|
|
604
|
-
status: "planned",
|
|
605
|
-
detail: nodeCount === void 0 ? "would execute the recipe nodes" : `would execute ${nodeCount} recipe node(s)`
|
|
606
|
-
}
|
|
607
|
-
];
|
|
608
|
-
const { redactRecipeParams } = await importRecipeHarness();
|
|
609
|
-
const payload = {
|
|
610
|
-
schemaVersion: 1,
|
|
611
|
-
command: "run",
|
|
612
|
-
mode: "plan",
|
|
613
|
-
status,
|
|
614
|
-
adapter,
|
|
615
|
-
recipe: recipeFile,
|
|
616
|
-
params: redactRecipeParams(effectiveParams, isRecord(recipe) ? recipe.paramsSchema : void 0).params,
|
|
617
|
-
findings,
|
|
618
|
-
plan
|
|
619
|
-
};
|
|
620
|
-
if (status === "fail") {
|
|
621
|
-
payload.error = {
|
|
622
|
-
code: "RECIPE_VALIDATION_FAILED",
|
|
623
|
-
message: `recipe validation found ${errorCount} error(s)`,
|
|
624
|
-
...capabilityRefusals.length > 0 ? { missingCapabilities: capabilityRefusals } : {},
|
|
625
|
-
userAction: failureUserAction
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
if (stream.enabled) {
|
|
629
|
-
if (status === "fail") stream.error(payload.error);
|
|
630
|
-
stream.complete(status, status === "pass" ? EXIT.ok : EXIT.validation, { mode: "plan", adapter, recipe: recipeFile, findings, plan });
|
|
631
|
-
} else if (json) {
|
|
632
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
633
|
-
} else {
|
|
634
|
-
console.log(`plan ${status} \u2014 ${adapter} \u2014 ${recipeFile}`);
|
|
635
|
-
for (const item of plan) {
|
|
636
|
-
const mark = item.status === "error" ? "\u2717" : item.status === "ok" ? "\u2713" : "\xB7";
|
|
637
|
-
console.log(` ${mark} [${item.confidence}] ${item.step}: ${item.detail}`);
|
|
638
|
-
}
|
|
639
|
-
if (findings.length) {
|
|
640
|
-
console.log("findings:");
|
|
641
|
-
for (const finding of findings) {
|
|
642
|
-
console.log(` ${finding.severity === "error" ? "\u2717" : "\u26A0"} ${finding.code} ${finding.path} \u2014 ${finding.message}`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
if (status === "fail") console.error(` Next: ${failureUserAction}`);
|
|
646
|
-
}
|
|
647
|
-
return status === "pass" ? EXIT.ok : EXIT.validation;
|
|
648
|
-
}
|
|
649
|
-
function emitPlanUsageError(json, stream, adapter, recipeFile, code, message, userAction) {
|
|
650
|
-
stream.error({ code, message, userAction, mode: "plan", adapter, recipe: recipeFile });
|
|
651
|
-
if (json) {
|
|
652
|
-
console.log(
|
|
653
|
-
JSON.stringify(
|
|
654
|
-
{ schemaVersion: 1, command: "run", mode: "plan", status: "fail", adapter, recipe: recipeFile, error: { code, message, userAction } },
|
|
655
|
-
null,
|
|
656
|
-
2
|
|
657
|
-
)
|
|
658
|
-
);
|
|
659
|
-
} else {
|
|
660
|
-
console.error(`\u2717 run --plan: ${message}
|
|
661
|
-
Next: ${userAction}`);
|
|
662
|
-
}
|
|
663
|
-
return EXIT.usage;
|
|
664
|
-
}
|
|
665
|
-
function emitRunRecipeRunning(json, stream, target, detail) {
|
|
666
|
-
const message = detail ?? "a recipe is currently running \u2014 refusing to start while another recipe executes.";
|
|
667
|
-
const userAction = `inspect the checkout state with: mm-harness status --target ${shellQuote(target)} --json; retry after the active recipe finishes`;
|
|
668
|
-
stream.error({ code: "RECIPE_RUNNING", message, userAction, recoverable: false });
|
|
669
|
-
if (json) {
|
|
670
|
-
console.log(JSON.stringify({ schemaVersion: 1, status: "fail", recoverable: false, error: { code: "RECIPE_RUNNING", message, userAction } }, null, 2));
|
|
671
|
-
} else {
|
|
672
|
-
console.error(`\u2717 mm-harness: ${message}
|
|
673
|
-
Next: ${userAction}`);
|
|
674
|
-
}
|
|
675
|
-
return EXIT.bounded;
|
|
676
|
-
}
|
|
677
|
-
function emitRunUsageError(json, stream, adapter, recipeFile, code, message, userAction) {
|
|
678
|
-
stream.error({ code, message, userAction });
|
|
679
|
-
if (json) {
|
|
680
|
-
console.log(
|
|
681
|
-
JSON.stringify(
|
|
682
|
-
{ schemaVersion: 1, command: "run", adapter, status: "fail", exitCode: EXIT.usage, recipe: recipeFile, error: { code, message, userAction } },
|
|
683
|
-
null,
|
|
684
|
-
2
|
|
685
|
-
)
|
|
686
|
-
);
|
|
687
|
-
} else {
|
|
688
|
-
console.error(`\u2717 run: ${message}`);
|
|
689
|
-
console.error(` Next: ${userAction}`);
|
|
690
|
-
}
|
|
691
|
-
return EXIT.usage;
|
|
692
|
-
}
|
|
693
|
-
function emitRunValidationError(json, stream, adapter, recipeFile, findings, errorCount, capabilityRefusals, libraryContextArgs) {
|
|
694
|
-
const message = `recipe validation found ${errorCount} error(s)`;
|
|
695
|
-
const userAction = capabilityRefusals.length > 0 ? capabilityValidationUserAction(adapter, capabilityRefusals, libraryContextArgs) : runPlanProbe(adapter, recipeFile);
|
|
696
|
-
const capabilityDetails = capabilityRefusals.length > 0 ? { missingCapabilities: capabilityRefusals } : {};
|
|
697
|
-
stream.error({
|
|
698
|
-
code: "RECIPE_VALIDATION_FAILED",
|
|
699
|
-
message,
|
|
700
|
-
userAction,
|
|
701
|
-
findings,
|
|
702
|
-
...capabilityDetails
|
|
703
|
-
});
|
|
704
|
-
if (json) {
|
|
705
|
-
console.log(
|
|
706
|
-
JSON.stringify(
|
|
707
|
-
{
|
|
708
|
-
schemaVersion: 1,
|
|
709
|
-
command: "run",
|
|
710
|
-
adapter,
|
|
711
|
-
status: "fail",
|
|
712
|
-
exitCode: EXIT.validation,
|
|
713
|
-
recovered: [],
|
|
714
|
-
mutations: [],
|
|
715
|
-
recipe: recipeFile,
|
|
716
|
-
findings,
|
|
717
|
-
error: {
|
|
718
|
-
code: "RECIPE_VALIDATION_FAILED",
|
|
719
|
-
message,
|
|
720
|
-
userAction,
|
|
721
|
-
...capabilityDetails
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
null,
|
|
725
|
-
2
|
|
726
|
-
)
|
|
727
|
-
);
|
|
728
|
-
} else {
|
|
729
|
-
console.error(`\u2717 run: ${message}`);
|
|
730
|
-
for (const finding of findings) {
|
|
731
|
-
if (finding.severity === "error") console.error(` ${finding.code} ${finding.path} \u2014 ${finding.message}`);
|
|
732
|
-
}
|
|
733
|
-
console.error(` Next: ${userAction}`);
|
|
734
|
-
}
|
|
735
|
-
return EXIT.validation;
|
|
736
|
-
}
|
|
737
|
-
function runPlanProbe(adapter, recipeFile) {
|
|
738
|
-
return `mm-harness run --plan ${shellQuote(recipeFile)} --adapter ${adapter} --json`;
|
|
739
|
-
}
|
|
740
|
-
function runUsageRecovery(code, message, adapter, recipeFile) {
|
|
741
|
-
const actionCommand = /This is an action, not a recipe\. Use: (mm-harness call .+)\.$/u.exec(message)?.[1];
|
|
742
|
-
if (actionCommand) return actionCommand;
|
|
743
|
-
if (code === "RECIPE_NOT_FOUND") return `mm-harness run --list --adapter ${adapter} --json`;
|
|
744
|
-
if (code === "RECIPE_UNPARSEABLE") {
|
|
745
|
-
return `fix the JSON syntax in ${shellQuote(recipeFile)}, then retry: ${runPlanProbe(adapter, recipeFile)}`;
|
|
746
|
-
}
|
|
747
|
-
return runPlanProbe(adapter, recipeFile);
|
|
748
|
-
}
|
|
749
|
-
export {
|
|
750
|
-
handleRun
|
|
751
|
-
};
|