@deeeed/metamask-harness 0.7.0 → 0.7.2
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 +19 -0
- package/adapters/core/inject.sh +7 -2
- package/adapters/extension/inject.mjs +9 -2
- package/adapters/mobile/inject.sh +7 -2
- package/dist/cli-commands.js +3 -2
- package/dist/cli.js +2 -0
- package/dist/commands/doctor.js +34 -1
- package/dist/commands/launch/index.js +1 -3
- package/dist/commands/parse-args.js +1 -0
- package/dist/commands/recipe-quality.js +93 -0
- package/dist/mm-harness-cli.js +39 -2
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`recipe-quality build`** — the single worker surface for producing `recipe-quality.json`: fronts @farmslot/agent-runtime's canonical builder (no reimplementation), validates against @farmslot/protocol `RecipeQualityArtifact` before writing (invalid input writes nothing, exit 5 with a teaching escape naming the invalid field). New production dependency: `@farmslot/agent-runtime`.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- `@farmslot/protocol` dependency raised to `^0.7.3` so one protocol version serves both the harness and the builder.
|
|
10
|
+
- The packed-install contract test now executes `recipe-quality build` from the published layout, guarding the new dependency's packaging path.
|
|
11
|
+
|
|
12
|
+
## 0.7.1
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`doctor --expect-live`** — exit-coded runtime liveness for hooks and scripts: exit 0 only when the adapter runtime decision is `ready` (extension: watcher + CDP; mobile: metro + bridge; core: deps), non-zero with a teaching escape otherwise. Fails closed on every degraded path; reuses the doctor runtime probe, no duplicated checks.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **Per-command `--help` documents every implemented flag** (`call/run --list`, `--library`, `--action-manifest`, `logs --window`, `actions --action`, `launch --url`, `doctor --runtime-dir`, `stop --adapter`) and tab-completion matches — the CLI self-teaches its own surface. A contract test guards help/implementation parity.
|
|
19
|
+
- **Injected runner shims resolve at run time** instead of a path baked at inject time: `MM_HARNESS_BIN` → global `mm-harness` on PATH → the recorded install-time path as last resort → teaching escape. A slot copied to or shared with another machine no longer points at a missing absolute path.
|
|
20
|
+
- Removed two parsed-but-unused `launch` flags (`--yes`, `--json-stream`).
|
|
21
|
+
|
|
3
22
|
## 0.7.0
|
|
4
23
|
|
|
5
24
|
The "version people use": every remaining identified fix/improvement batched on the
|
package/adapters/core/inject.sh
CHANGED
|
@@ -88,8 +88,13 @@ install_v1_runner_assets() {
|
|
|
88
88
|
if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
|
|
89
89
|
printf 'export FARMSLOT_ROOT=${FARMSLOT_ROOT:-%s}\n' "$runner_protocol_root_q"
|
|
90
90
|
fi
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
# Resolve the runner at run time (override > global install > install-time
|
|
92
|
+
# record); the recorded path is last resort so a moved checkout is not pinned.
|
|
93
|
+
printf '%s\n' 'if [ -n "${MM_HARNESS_BIN:-}" ] && [ -x "${MM_HARNESS_BIN}" ]; then exec "${MM_HARNESS_BIN}" "$@"; fi'
|
|
94
|
+
printf '%s\n' 'if command -v mm-harness >/dev/null 2>&1; then exec mm-harness "$@"; fi'
|
|
95
|
+
printf 'if [ -x %s ]; then exec %s "$@"; fi\n' "$runner_exec_q" "$runner_exec_q"
|
|
96
|
+
printf '%s\n' 'echo "mm-harness not found. Next: npm i -g @deeeed/metamask-harness" >&2'
|
|
97
|
+
printf '%s\n' 'exit 127'
|
|
93
98
|
} > "$HARNESS_DIR/runner/bin/mm-harness"
|
|
94
99
|
chmod +x "$HARNESS_DIR/runner/bin/mm-harness"
|
|
95
100
|
if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
|
|
@@ -68,12 +68,19 @@ fs.mkdirSync(path.join(harnessDir, 'runner/recipes'), { recursive: true });
|
|
|
68
68
|
fs.rmSync(path.join(harnessDir, 'scripts'), { recursive: true, force: true });
|
|
69
69
|
fs.mkdirSync(path.join(harnessDir, 'scripts/lib'), { recursive: true });
|
|
70
70
|
|
|
71
|
+
// Resolve the runner at run time so a moved or shared checkout is never bound to
|
|
72
|
+
// an install-time absolute path: explicit override, then a global install, then
|
|
73
|
+
// the runner recorded at install time as a last resort; teach install on miss.
|
|
74
|
+
const pinnedBin = shellQuote(path.join(runnerDir, 'bin/mm-harness'));
|
|
71
75
|
const delegate = [
|
|
72
76
|
'#!/usr/bin/env bash',
|
|
73
77
|
'set -euo pipefail',
|
|
74
78
|
protocolRoot ? `export FARMSLOT_ROOT=\${FARMSLOT_ROOT:-${shellQuote(protocolRoot)}}` : null,
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
'if [ -n "${MM_HARNESS_BIN:-}" ] && [ -x "${MM_HARNESS_BIN}" ]; then exec "${MM_HARNESS_BIN}" "$@"; fi',
|
|
80
|
+
'if command -v mm-harness >/dev/null 2>&1; then exec mm-harness "$@"; fi',
|
|
81
|
+
`if [ -x ${pinnedBin} ]; then exec ${pinnedBin} "$@"; fi`,
|
|
82
|
+
'echo "mm-harness not found. Next: npm i -g @deeeed/metamask-harness" >&2',
|
|
83
|
+
'exit 127',
|
|
77
84
|
].filter(Boolean).join('\n') + '\n';
|
|
78
85
|
fs.writeFileSync(path.join(harnessDir, 'runner/bin/mm-harness'), delegate, { mode: 0o755 });
|
|
79
86
|
if (protocolRoot) fs.writeFileSync(path.join(harnessDir, 'runner/.farmslot-root'), `${protocolRoot}\n`);
|
|
@@ -187,8 +187,13 @@ install_v1_runner_assets() {
|
|
|
187
187
|
if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
|
|
188
188
|
printf 'export FARMSLOT_ROOT=${FARMSLOT_ROOT:-%s}\n' "$runner_protocol_root_q"
|
|
189
189
|
fi
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
# Resolve the runner at run time (override > global install > install-time
|
|
191
|
+
# record); the recorded path is last resort so a moved checkout is not pinned.
|
|
192
|
+
printf '%s\n' 'if [ -n "${MM_HARNESS_BIN:-}" ] && [ -x "${MM_HARNESS_BIN}" ]; then exec "${MM_HARNESS_BIN}" "$@"; fi'
|
|
193
|
+
printf '%s\n' 'if command -v mm-harness >/dev/null 2>&1; then exec mm-harness "$@"; fi'
|
|
194
|
+
printf 'if [ -x %s ]; then exec %s "$@"; fi\n' "$runner_exec_q" "$runner_exec_q"
|
|
195
|
+
printf '%s\n' 'echo "mm-harness not found. Next: npm i -g @deeeed/metamask-harness" >&2'
|
|
196
|
+
printf '%s\n' 'exit 127'
|
|
192
197
|
} > "$HARNESS_DIR/runner/bin/mm-harness"
|
|
193
198
|
chmod +x "$HARNESS_DIR/runner/bin/mm-harness"
|
|
194
199
|
if [ -n "$METAMASK_RUNNER_PROTOCOL_ROOT" ]; then
|
package/dist/cli-commands.js
CHANGED
|
@@ -11,11 +11,12 @@ const SPEC = {
|
|
|
11
11
|
{ name: "ports", desc: "Slot ports and runtime paths", flags: ["--json"] },
|
|
12
12
|
{ name: "up", desc: "Decide + run minimum work to reach ready", flags: ["--json", "--dry-run"] },
|
|
13
13
|
{ name: "sync", desc: "Refresh harness + canonicalize wallet fixture", flags: ["--json"] },
|
|
14
|
-
{ name: "logs", aliases: ["tail"], desc: "Compact build events or full log", flags: ["--full", "-f"] },
|
|
14
|
+
{ name: "logs", aliases: ["tail"], desc: "Compact build events or full log", flags: ["--full", "-f", "--window", "--events", "--source", "--json"] },
|
|
15
15
|
{ name: "debug", aliases: ["devtools", "inspect"], desc: "Open DevTools UI", flags: ["--json", "--no-open"] },
|
|
16
16
|
{ name: "actions", desc: "List runnable recipe actions", flags: ["--json"] },
|
|
17
|
-
{ name: "doctor", desc: "Check harness/orchestration health", flags: ["--json", "--target", "--adapter", "--runtime-dir"] },
|
|
17
|
+
{ name: "doctor", desc: "Check harness/orchestration health", flags: ["--json", "--target", "--adapter", "--runtime-dir", "--expect-live", "--cdp-port"] },
|
|
18
18
|
{ name: "run", desc: "Execute a proof recipe", args: ["recipe.json"], flags: ["--list"] },
|
|
19
|
+
{ name: "recipe-quality", desc: "Build the recipe-quality artifact from compact JSON", args: ["build"], flags: ["--input", "--output", "--json"] },
|
|
19
20
|
{ name: "interactive", aliases: ["menu"], desc: "Interactive command menu" },
|
|
20
21
|
{ name: "prepare", desc: "Install harness (+ optional validate)", flags: ["--target", "--runtime-dir", "--json"] },
|
|
21
22
|
{ name: "runtime-status", desc: "Structured runtime status JSON", flags: ["--json", "--target", "--cdp-port", "--runtime-dir"] }
|
package/dist/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ import { handleLaunch } from "./commands/launch/index.js";
|
|
|
17
17
|
import { handleLogs } from "./commands/logs.js";
|
|
18
18
|
import { handleDebug } from "./commands/debug.js";
|
|
19
19
|
import { handleFixtures } from "./commands/fixtures.js";
|
|
20
|
+
import { handleRecipeQuality } from "./commands/recipe-quality.js";
|
|
20
21
|
import { parseArgs, targetPath } from "./commands/parse-args.js";
|
|
21
22
|
import { runOneNode } from "./commands/run-engine.js";
|
|
22
23
|
const COMMANDS = {
|
|
@@ -121,6 +122,7 @@ async function main(argv) {
|
|
|
121
122
|
if (command === "logs") return handleLogs(argv.slice(1));
|
|
122
123
|
if (command === "debug") return handleDebug(argv.slice(1));
|
|
123
124
|
if (command === "fixtures") return handleFixtures(argv.slice(1), { runOneNode });
|
|
125
|
+
if (command === "recipe-quality") return handleRecipeQuality(argv.slice(1));
|
|
124
126
|
const handler = COMMANDS[command];
|
|
125
127
|
if (!handler) throw new Error(`Unknown command: ${command}`);
|
|
126
128
|
return handler(parseArgs(argv.slice(1), command));
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,7 +7,7 @@ import { assertAdapter, runnerDir } from "../paths.js";
|
|
|
7
7
|
import { getAdapterSurface } from "../adapters/surface.js";
|
|
8
8
|
import { loadActionManifest, validateManifest } from "../manifest.js";
|
|
9
9
|
import { ensureOverlay, newHealState, recipeRunning } from "../heal-bounds.js";
|
|
10
|
-
import { ADAPTER_DETECT_NEXT, usageOut } from "./shared.js";
|
|
10
|
+
import { ADAPTER_DETECT_NEXT, EXIT, usageOut } from "./shared.js";
|
|
11
11
|
import {
|
|
12
12
|
actionManifestPathOption,
|
|
13
13
|
applyRuntimeDirOption,
|
|
@@ -15,10 +15,24 @@ import {
|
|
|
15
15
|
optionString,
|
|
16
16
|
targetPath
|
|
17
17
|
} from "./parse-args.js";
|
|
18
|
+
function applyDoctorRuntimePorts(options) {
|
|
19
|
+
const cdpPort = optionString(options, "cdpPort");
|
|
20
|
+
if (cdpPort) {
|
|
21
|
+
process.env.CDP_PORT = cdpPort;
|
|
22
|
+
process.env.RECIPE_CDP_PORT = cdpPort;
|
|
23
|
+
}
|
|
24
|
+
const watcherPort = optionString(options, "watcherPort") ?? optionString(options, "port") ?? optionString(options, "metroPort");
|
|
25
|
+
if (watcherPort) {
|
|
26
|
+
process.env.WATCHER_PORT = watcherPort;
|
|
27
|
+
process.env.METRO_PORT = watcherPort;
|
|
28
|
+
process.env.RECIPE_WATCHER_PORT = watcherPort;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
18
31
|
async function handleDoctor({ options }) {
|
|
19
32
|
applyRuntimeDirOption(options);
|
|
20
33
|
const target = targetPath(options);
|
|
21
34
|
const json = optionFlag(options, "json");
|
|
35
|
+
const expectLive = optionFlag(options, "expectLive");
|
|
22
36
|
const explicitAdapter = optionString(options, "adapter") ?? optionString(options, "platform");
|
|
23
37
|
const adapter = explicitAdapter ?? detectAdapter(target);
|
|
24
38
|
if (!adapter) {
|
|
@@ -40,11 +54,13 @@ async function handleDoctor({ options }) {
|
|
|
40
54
|
try {
|
|
41
55
|
const surface = getAdapterSurface(adapter);
|
|
42
56
|
surface.resolveSlotPorts(target);
|
|
57
|
+
applyDoctorRuntimePorts(options);
|
|
43
58
|
runtime = await surface.runtimeStatus(target);
|
|
44
59
|
} catch {
|
|
45
60
|
}
|
|
46
61
|
const orphanMetros = adapter === "mobile" ? detectOrphanMetros(target, process.env.WATCHER_PORT) : [];
|
|
47
62
|
const capture = captureHelperHealth();
|
|
63
|
+
if (expectLive) return emitExpectLive(adapter, target, runtime, result, orphanMetros, capture, json);
|
|
48
64
|
if (json) console.log(JSON.stringify({ ...result, runtime, orphanMetros, capture }, null, 2));
|
|
49
65
|
else {
|
|
50
66
|
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
@@ -78,6 +94,23 @@ async function handleDoctor({ options }) {
|
|
|
78
94
|
}
|
|
79
95
|
return result.status === "pass" ? 0 : 1;
|
|
80
96
|
}
|
|
97
|
+
function emitExpectLive(adapter, target, runtime, result, orphanMetros, capture, json) {
|
|
98
|
+
const live = runtime?.decision === "ready";
|
|
99
|
+
if (json) {
|
|
100
|
+
console.log(JSON.stringify({ ...result, runtime, orphanMetros, capture }, null, 2));
|
|
101
|
+
return live ? EXIT.ok : EXIT.runtime;
|
|
102
|
+
}
|
|
103
|
+
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
104
|
+
if (live) {
|
|
105
|
+
console.log(`${out("ok", "live")} ${out("bold", adapter)} runtime ready`);
|
|
106
|
+
return EXIT.ok;
|
|
107
|
+
}
|
|
108
|
+
const detail = runtime ? `${runtime.decision}${runtime.reasonCode ? ` (${runtime.reasonCode})` : ""}` : "runtime probe unavailable";
|
|
109
|
+
console.error(`${out("err", "not-live")} ${out("bold", adapter)} ${detail} ${out("dim", target)}`);
|
|
110
|
+
for (const reason of runtime?.reasons ?? []) console.error(` ${out("dim", reason)}`);
|
|
111
|
+
console.error(` Next: ${getAdapterSurface(adapter).hints.relaunch}`);
|
|
112
|
+
return EXIT.runtime;
|
|
113
|
+
}
|
|
81
114
|
function captureHelperHealth() {
|
|
82
115
|
if (process.platform !== "darwin") return null;
|
|
83
116
|
const bin = process.env.CAPTURE_HELPER_PATH || "capture-helper";
|
|
@@ -32,9 +32,7 @@ const LAUNCH_BOOLEANS = /* @__PURE__ */ new Set([
|
|
|
32
32
|
"sidepanel",
|
|
33
33
|
"fullscreen",
|
|
34
34
|
"runway",
|
|
35
|
-
"json"
|
|
36
|
-
"jsonStream",
|
|
37
|
-
"yes"
|
|
35
|
+
"json"
|
|
38
36
|
]);
|
|
39
37
|
async function handleLaunch(argv) {
|
|
40
38
|
const { positional, options } = parseFlags(argv, LAUNCH_BOOLEANS);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
buildRecipeQualityArtifact
|
|
5
|
+
} from "@farmslot/agent-runtime";
|
|
6
|
+
import { EXIT, usageOut } from "./shared.js";
|
|
7
|
+
import { optionFlag, optionString, parseArgs } from "./parse-args.js";
|
|
8
|
+
const BUILD_USAGE = "mm-harness recipe-quality build --input <compact.json> --output <path> [--json]";
|
|
9
|
+
function errorMessage(error) {
|
|
10
|
+
return error instanceof Error ? error.message : String(error);
|
|
11
|
+
}
|
|
12
|
+
function invalidOut(json, message, source) {
|
|
13
|
+
const userAction = `fix ${source} (${message}), then re-run: ${BUILD_USAGE}`;
|
|
14
|
+
if (json) {
|
|
15
|
+
console.log(
|
|
16
|
+
JSON.stringify(
|
|
17
|
+
{
|
|
18
|
+
schemaVersion: 1,
|
|
19
|
+
command: "recipe-quality",
|
|
20
|
+
action: "build",
|
|
21
|
+
status: "fail",
|
|
22
|
+
exitCode: EXIT.validation,
|
|
23
|
+
error: { code: "RECIPE_QUALITY_INVALID", message, userAction }
|
|
24
|
+
},
|
|
25
|
+
null,
|
|
26
|
+
2
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
} else {
|
|
30
|
+
console.error(`\u2717 mm-harness recipe-quality: ${message}
|
|
31
|
+
Next: ${userAction}`);
|
|
32
|
+
}
|
|
33
|
+
return EXIT.validation;
|
|
34
|
+
}
|
|
35
|
+
async function handleRecipeQuality(argv) {
|
|
36
|
+
const { positional, options } = parseArgs(argv, "recipe-quality");
|
|
37
|
+
const json = optionFlag(options, "json");
|
|
38
|
+
const action = positional[0];
|
|
39
|
+
if (action !== "build") {
|
|
40
|
+
const message = action ? `unknown action '${action}'` : "missing action";
|
|
41
|
+
return usageOut(json, "recipe-quality", message, BUILD_USAGE);
|
|
42
|
+
}
|
|
43
|
+
return buildArtifact(options, json);
|
|
44
|
+
}
|
|
45
|
+
function buildArtifact(options, json) {
|
|
46
|
+
const input = optionString(options, "input");
|
|
47
|
+
const output = optionString(options, "output");
|
|
48
|
+
if (!input) return usageOut(json, "recipe-quality", "missing --input <compact.json>", BUILD_USAGE);
|
|
49
|
+
if (!output) return usageOut(json, "recipe-quality", "missing --output <path>", BUILD_USAGE);
|
|
50
|
+
const inputPath = path.resolve(input);
|
|
51
|
+
let raw;
|
|
52
|
+
try {
|
|
53
|
+
raw = fs.readFileSync(inputPath, "utf8");
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return usageOut(
|
|
56
|
+
json,
|
|
57
|
+
"recipe-quality",
|
|
58
|
+
`cannot read --input ${input}: ${errorMessage(error)}`,
|
|
59
|
+
`write the compact recipe-quality JSON to that path, then: ${BUILD_USAGE}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(raw);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return invalidOut(json, `--input ${input} is not valid JSON: ${errorMessage(error)}`, `--input ${input}`);
|
|
67
|
+
}
|
|
68
|
+
let artifact;
|
|
69
|
+
try {
|
|
70
|
+
artifact = buildRecipeQualityArtifact(parsed);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return invalidOut(json, errorMessage(error), `--input ${input}`);
|
|
73
|
+
}
|
|
74
|
+
const outputPath = path.resolve(output);
|
|
75
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
76
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(artifact, null, 2)}
|
|
77
|
+
`);
|
|
78
|
+
if (json) {
|
|
79
|
+
console.log(
|
|
80
|
+
JSON.stringify(
|
|
81
|
+
{ schemaVersion: 1, command: "recipe-quality", action: "build", status: "ok", outputPath: output, artifact },
|
|
82
|
+
null,
|
|
83
|
+
2
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
} else {
|
|
87
|
+
console.log(`\u2713 recipe-quality build \u2192 ${output} (verdict: ${artifact.verdict})`);
|
|
88
|
+
}
|
|
89
|
+
return EXIT.ok;
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
handleRecipeQuality
|
|
93
|
+
};
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const REAL = [
|
|
|
18
18
|
|
|
19
19
|
List the action vocabulary + field schemas for the checkout adapter.
|
|
20
20
|
|
|
21
|
+
--action <name> Describe one action; fuzzy-resolves like call (short or full name)
|
|
21
22
|
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
22
23
|
--target <path> Checkout path (default: cwd)
|
|
23
24
|
--raw Dump raw action registry JSON
|
|
@@ -25,6 +26,7 @@ const REAL = [
|
|
|
25
26
|
|
|
26
27
|
Example:
|
|
27
28
|
mm-harness actions --adapter mobile
|
|
29
|
+
mm-harness actions --adapter mobile --action assert_orders
|
|
28
30
|
mm-harness actions --adapter extension --raw`
|
|
29
31
|
},
|
|
30
32
|
{
|
|
@@ -41,6 +43,7 @@ Example:
|
|
|
41
43
|
core headless \u2014 no dev server to stop (teaching error)
|
|
42
44
|
|
|
43
45
|
--port <port> Dev-server port (default: the checkout's slot context)
|
|
46
|
+
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
44
47
|
--target <path> Checkout path (default: cwd)
|
|
45
48
|
--json Machine-readable output
|
|
46
49
|
|
|
@@ -59,11 +62,13 @@ Example:
|
|
|
59
62
|
if unique; ambiguous = exit 2. Actions differ per adapter \u2014 list this checkout's
|
|
60
63
|
with: mm-harness actions.
|
|
61
64
|
|
|
65
|
+
--list List everything invocable for the adapter (actions + flows); no <action> needed
|
|
62
66
|
--arg k=v Action field value (repeatable)
|
|
63
67
|
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
64
68
|
--target <path> Checkout path (default: cwd)
|
|
65
69
|
--artifacts-dir <dir> Where to write evidence (default: temp dir)
|
|
66
70
|
--action-manifest <path> Override the action manifest
|
|
71
|
+
--library <name=path> Add/override a recipe-library source (repeatable)
|
|
67
72
|
--heal <off|infra-only|auto> Healing policy (default: infra-only); auto-ensures the overlay
|
|
68
73
|
--json Machine-readable output
|
|
69
74
|
|
|
@@ -96,10 +101,13 @@ Example:
|
|
|
96
101
|
|
|
97
102
|
Validate + run a recipe and write evidence (summary / trace / artifacts).
|
|
98
103
|
|
|
104
|
+
--list List everything invocable for the adapter (actions + flows); no <recipe> needed
|
|
99
105
|
--plan Validate + print execution plan, touching nothing. Exit 5 if invalid.
|
|
100
106
|
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
101
107
|
--target <path> Checkout path (default: cwd)
|
|
102
108
|
--artifacts-dir <dir> Where to write evidence (required unless --plan)
|
|
109
|
+
--action-manifest <path> Override the action manifest
|
|
110
|
+
--library <name=path> Add/override a recipe-library source (repeatable)
|
|
103
111
|
--heal <off|infra-only|auto> Healing policy (default: infra-only); auto-ensures the overlay
|
|
104
112
|
--json Machine-readable output
|
|
105
113
|
--record-video=full-run Record a video of the run
|
|
@@ -118,15 +126,41 @@ Example:
|
|
|
118
126
|
sections so there is no hunting for files.
|
|
119
127
|
|
|
120
128
|
--fix Repair the overlay/runtime-context WITHOUT launching (no fixture reseed); --json adds fixed[]/failed[]
|
|
129
|
+
--expect-live Exit 0 iff the runtime is live (extension: watcher+CDP; mobile: Metro+bridge; core: deps), non-zero + teaching escape otherwise
|
|
130
|
+
--cdp-port <port> Extension CDP port for the liveness probe (env: CDP_PORT / RECIPE_CDP_PORT)
|
|
121
131
|
--adapter <mobile|extension|core> Target adapter (auto-detected inside a checkout)
|
|
122
132
|
--target <path> Checkout path (default: cwd)
|
|
133
|
+
--runtime-dir <dir> Runtime dir containing agentic-runtime.json (relative to target)
|
|
123
134
|
--json Machine-readable output
|
|
124
135
|
|
|
125
136
|
Example:
|
|
126
137
|
mm-harness doctor
|
|
127
138
|
mm-harness doctor --fix --json
|
|
139
|
+
mm-harness doctor --adapter extension --target /path/to/checkout --cdp-port 6662 --expect-live
|
|
128
140
|
mm-harness doctor --adapter mobile --target /path/to/checkout`
|
|
129
141
|
},
|
|
142
|
+
{
|
|
143
|
+
name: "recipe-quality",
|
|
144
|
+
summary: "Build the recipe-quality artifact from a compact verdict JSON (validates before writing).",
|
|
145
|
+
example: "mm-harness recipe-quality build --input compact.json --output artifacts/recipe-quality.json",
|
|
146
|
+
helpText: `mm-harness recipe-quality build [flags]
|
|
147
|
+
|
|
148
|
+
Build artifacts/recipe-quality.json from the compact fields a recipe-quality pass
|
|
149
|
+
produces (verdict + reasons + optional guidance/dimensions/findings/delta/training).
|
|
150
|
+
The artifact is validated against the RecipeQualityArtifact schema before it is
|
|
151
|
+
written \u2014 an invalid input never reaches disk.
|
|
152
|
+
|
|
153
|
+
--input <compact.json> Compact verdict JSON: { "verdict": "pass|warn|fail", "reasons": [..],
|
|
154
|
+
"betterVersionGuidance"?: [..], "dimensions"?: {..}, "trainingFields"?: {..}, \u2026 }
|
|
155
|
+
--output <path> Where to write the built artifact (parent dirs created)
|
|
156
|
+
--json Machine-readable envelope { status, outputPath, artifact }
|
|
157
|
+
|
|
158
|
+
Exit: 0 built \xB7 2 missing/unreadable args \xB7 5 invalid input (teaching escape names the invalid field).
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
mm-harness recipe-quality build --input compact.json --output artifacts/recipe-quality.json
|
|
162
|
+
mm-harness recipe-quality build --input compact.json --output artifacts/recipe-quality.json --json`
|
|
163
|
+
},
|
|
130
164
|
{
|
|
131
165
|
name: "provision",
|
|
132
166
|
summary: "Install the cached Runway iOS dev client on a prepared mobile slot (no deps, no Metro).",
|
|
@@ -238,6 +272,7 @@ Example:
|
|
|
238
272
|
--runway Post-launch runway check (mobile only; teaching error elsewhere)
|
|
239
273
|
--watch Persistent webpack watcher then relaunch (extension only)
|
|
240
274
|
--sidepanel | --fullscreen Extension display mode (default --fullscreen)
|
|
275
|
+
--url <dapp-url> Open a dapp in the main tab beside the sidepanel (extension only)
|
|
241
276
|
--heal <off|infra-only|auto> Healing policy (default auto); bounds always enforced
|
|
242
277
|
--device <udid|name> Target simulator/device (env: IOS_SIMULATOR / ADB_SERIAL)
|
|
243
278
|
--cdp-port <port> Extension CDP port (env: CDP_PORT / RECIPE_CDP_PORT)
|
|
@@ -261,6 +296,7 @@ Example:
|
|
|
261
296
|
Teaching error if nothing is running (points at launch).
|
|
262
297
|
|
|
263
298
|
--full Raw log tail (default = compact) (env: RECIPE_LOG_UI)
|
|
299
|
+
--window (Re)open the read-only tmux tail window for the resolved dev-server port (leaves the process untouched)
|
|
264
300
|
--events <n> Compact event count (default 10) (env: RECIPE_LOG_EVENTS)
|
|
265
301
|
--source <label> Log source per adapter \u2014 mobile: metro|app (default metro);
|
|
266
302
|
extension: webpack|watcher|rebuild|app (default webpack).
|
|
@@ -271,7 +307,8 @@ Example:
|
|
|
271
307
|
|
|
272
308
|
Example:
|
|
273
309
|
mm-harness logs
|
|
274
|
-
mm-harness logs --full
|
|
310
|
+
mm-harness logs --full
|
|
311
|
+
mm-harness logs --window`
|
|
275
312
|
},
|
|
276
313
|
{
|
|
277
314
|
name: "debug",
|
|
@@ -378,7 +415,7 @@ const HELP_GROUPS = [
|
|
|
378
415
|
{
|
|
379
416
|
title: "PROVE",
|
|
380
417
|
blurb: "run recipes and inspect readiness",
|
|
381
|
-
commands: ["run", "doctor"]
|
|
418
|
+
commands: ["run", "doctor", "recipe-quality"]
|
|
382
419
|
},
|
|
383
420
|
{
|
|
384
421
|
title: "RUNTIME OVERLAY",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@farmslot/
|
|
19
|
+
"@farmslot/agent-runtime": "^0.1.0",
|
|
20
|
+
"@farmslot/protocol": "^0.7.3",
|
|
20
21
|
"@farmslot/recipe-harness": "^0.3.3",
|
|
21
22
|
"commander": "^12.0.0",
|
|
22
23
|
"viem": "^2.54.3"
|