@deeeed/metamask-harness 0.60.0 → 0.61.1
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 +27 -0
- package/dist/commands/run.js +4 -3
- package/dist/commands/status-watch.js +32 -0
- package/dist/task-view.js +34 -1
- package/docs/RECIPES.md +9 -0
- package/library/actions/mobile/platform/bridge.mjs +2 -2
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.61.1 - 2026-09-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Preserve explicitly typed public signer-key indices in recipe plans while redacting credentials. Adopt `@farmslot/recipe-harness@0.18.1` invocation evidence so parameterized run artifacts validate without reconstructing inputs.
|
|
10
|
+
|
|
11
|
+
## 0.61.0 - 2026-09-20
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `status --watch` and `status --task` show child checklist units (Farmslot ADR-060): a unit registered on a row is read from `subtasks/index.json` beside the checklist and printed under the row that owns it, with its id, status, `done/total` and source, and its rows indented one level deeper. Piped output logs a registration, each child row tick and a child status move. Everything comes from the protocol helpers (`subtaskPaths`, `isSettledSubtaskStatus`, `enumerateChecklistCheckboxes`), so the harness parses nothing itself.
|
|
16
|
+
- Pins `@farmslot/protocol` 0.30.0, `@farmslot/agent-runtime` 0.13.0, `@farmslot/recipe-harness` 0.18.0 and `@farmslot/expo-recipe` 0.11.0 together, so one protocol copy serves the harness; a worker produces `subtasks/` once its node runs agent-runtime 0.13.0 (`mark sub`).
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Mobile `ui.screenshot` records the node's `label` on its artifact-manifest entry (simctl and adb paths); the bridge leaf used to stamp the generic `ui.screenshot screenshot` and drop the label the action schema declares, so packaged evidence carried no caption. The Extension path already carried it.
|
|
21
|
+
|
|
5
22
|
## 0.60.0 - 2026-09-19
|
|
6
23
|
|
|
7
24
|
### Changed
|
|
@@ -1494,6 +1511,11 @@ Fresh-install hotfixes found in live validation of 0.5.0.
|
|
|
1494
1511
|
|
|
1495
1512
|
## Unreleased
|
|
1496
1513
|
|
|
1514
|
+
### Added
|
|
1515
|
+
|
|
1516
|
+
- `status --watch` and `status --task` show child checklist units (Farmslot ADR-060): a unit registered on a row is read from `subtasks/index.json` beside the checklist and printed under the row that owns it, with its id, status, `done/total` and source, and its rows indented one level deeper. Piped output logs a registration, each child row tick and a child status move. Everything comes from the protocol helpers (`subtaskPaths`, `isSettledSubtaskStatus`, `enumerateChecklistCheckboxes`), so the harness parses nothing itself.
|
|
1517
|
+
- Pins `@farmslot/protocol` 0.30.0, `@farmslot/agent-runtime` 0.13.0, `@farmslot/recipe-harness` 0.18.0 and `@farmslot/expo-recipe` 0.11.0 together, so one protocol copy serves the harness; a worker produces `subtasks/` once its node runs agent-runtime 0.13.0 (`mark sub`).
|
|
1518
|
+
|
|
1497
1519
|
### Fixed
|
|
1498
1520
|
|
|
1499
1521
|
- **`provision --json` adapter-detection failures emit the standard error envelope** — usage errors now match `doctor`/`launch` (envelope on stdout with `error.userAction`; stderr stays human-only). Previously `--json` callers got empty stdout.
|
|
@@ -2382,6 +2404,11 @@ Fresh-install hotfixes found in live validation of 0.5.0.
|
|
|
2382
2404
|
|
|
2383
2405
|
## Unreleased
|
|
2384
2406
|
|
|
2407
|
+
### Added
|
|
2408
|
+
|
|
2409
|
+
- `status --watch` and `status --task` show child checklist units (Farmslot ADR-060): a unit registered on a row is read from `subtasks/index.json` beside the checklist and printed under the row that owns it, with its id, status, `done/total` and source, and its rows indented one level deeper. Piped output logs a registration, each child row tick and a child status move. Everything comes from the protocol helpers (`subtaskPaths`, `isSettledSubtaskStatus`, `enumerateChecklistCheckboxes`), so the harness parses nothing itself.
|
|
2410
|
+
- Pins `@farmslot/protocol` 0.30.0, `@farmslot/agent-runtime` 0.13.0, `@farmslot/recipe-harness` 0.18.0 and `@farmslot/expo-recipe` 0.11.0 together, so one protocol copy serves the harness; a worker produces `subtasks/` once its node runs agent-runtime 0.13.0 (`mark sub`).
|
|
2411
|
+
|
|
2385
2412
|
### Fixed
|
|
2386
2413
|
|
|
2387
2414
|
- **`provision --json` adapter-detection failures emit the standard error envelope** — usage errors now match `doctor`/`launch` (envelope on stdout with `error.userAction`; stderr stays human-only). Previously `--json` callers got empty stdout.
|
package/dist/commands/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { walletFixturePath } from "../paths.js";
|
|
3
|
+
import { importRecipeHarness, walletFixturePath } from "../paths.js";
|
|
4
4
|
import { color } from "../cli-color.js";
|
|
5
5
|
import { recipeRunning } from "../heal-bounds.js";
|
|
6
6
|
import { checkoutBusyOut, EXIT, usageOut, writeInteractiveProgress } from "./shared.js";
|
|
@@ -39,7 +39,7 @@ import { JsonStreamWriter } from "../json-stream.js";
|
|
|
39
39
|
import { formatRunDiagnosticsForHuman, readRunDiagnosticsDocument } from "../run-diagnostics.js";
|
|
40
40
|
import { recipeTrustFailure } from "../recipe-security.js";
|
|
41
41
|
import { ProvenanceDriftError } from "../execution-provenance.js";
|
|
42
|
-
import { recordCommandEvidence
|
|
42
|
+
import { recordCommandEvidence } from "../command-journal.js";
|
|
43
43
|
import {
|
|
44
44
|
actionLibraryContextArgs,
|
|
45
45
|
missingActionCapabilities,
|
|
@@ -640,6 +640,7 @@ async function handleRunPlan(recipeArg, params, options, stream) {
|
|
|
640
640
|
detail: nodeCount === void 0 ? "would execute the recipe nodes" : `would execute ${nodeCount} recipe node(s)`
|
|
641
641
|
}
|
|
642
642
|
];
|
|
643
|
+
const { redactRecipeParams } = await importRecipeHarness();
|
|
643
644
|
const payload = {
|
|
644
645
|
schemaVersion: 1,
|
|
645
646
|
command: "run",
|
|
@@ -647,7 +648,7 @@ async function handleRunPlan(recipeArg, params, options, stream) {
|
|
|
647
648
|
status,
|
|
648
649
|
adapter,
|
|
649
650
|
recipe: recipeFile,
|
|
650
|
-
params:
|
|
651
|
+
params: redactRecipeParams(effectiveParams, isRecord(recipe) ? recipe.paramsSchema : void 0).params,
|
|
651
652
|
findings,
|
|
652
653
|
plan
|
|
653
654
|
};
|
|
@@ -130,6 +130,9 @@ function renderTaskView(view, title, explicitTask = false) {
|
|
|
130
130
|
}
|
|
131
131
|
phase = row.phase;
|
|
132
132
|
lines.push(renderRow(row, view.currentStep, out));
|
|
133
|
+
for (const unit of view.subtasks.filter((entry) => entry.parentStep === row.number)) {
|
|
134
|
+
lines.push(...renderSubtask(unit, out));
|
|
135
|
+
}
|
|
133
136
|
}
|
|
134
137
|
const signal = view.signal;
|
|
135
138
|
if (signal) {
|
|
@@ -171,6 +174,21 @@ function changedLines(previous, next) {
|
|
|
171
174
|
if (!row.checked || before.get(row.number) === true) continue;
|
|
172
175
|
lines.push(`checked ${row.number} ${row.label}${row.durationMs !== void 0 ? ` (${formatDuration(row.durationMs)})` : ""}`);
|
|
173
176
|
}
|
|
177
|
+
const beforeUnits = new Map(previous.subtasks.map((unit) => [unit.id, unit]));
|
|
178
|
+
for (const unit of next.subtasks) {
|
|
179
|
+
const before2 = beforeUnits.get(unit.id);
|
|
180
|
+
if (!before2) {
|
|
181
|
+
lines.push(`subtask ${unit.id} registered on step ${unit.parentStep} (${unit.rows.length} rows)`);
|
|
182
|
+
}
|
|
183
|
+
const beforeRows = new Map((before2?.rows ?? []).map((row) => [row.number, row.checked]));
|
|
184
|
+
for (const row of unit.rows) {
|
|
185
|
+
if (!row.checked || beforeRows.get(row.number) === true) continue;
|
|
186
|
+
lines.push(`subtask ${unit.id} checked ${row.number} ${row.label}`);
|
|
187
|
+
}
|
|
188
|
+
if (before2 && before2.status !== unit.status) {
|
|
189
|
+
lines.push(`subtask ${unit.id} ${unit.status ?? "unknown"}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
174
192
|
if (previous.signal?.step !== next.signal?.step && next.signal?.step) {
|
|
175
193
|
lines.push(`step ${stepLabel(next.signal.step)}`);
|
|
176
194
|
}
|
|
@@ -198,6 +216,20 @@ function renderRow(row, currentStep, out) {
|
|
|
198
216
|
const text = row.checked ? out("dim", label) : out("bold", label);
|
|
199
217
|
return ` ${marker} ${out("dim", number)} ${text}${timing ? ` ${out("dim", timing)}` : ""}`;
|
|
200
218
|
}
|
|
219
|
+
function renderSubtask(unit, out) {
|
|
220
|
+
const done = unit.rows.filter((row) => row.checked).length;
|
|
221
|
+
const header = joinParts([
|
|
222
|
+
`${out("label", "subtask")} ${out("accent", unit.id)}`,
|
|
223
|
+
statusWord(out, unit.status),
|
|
224
|
+
`${done}/${unit.rows.length}`,
|
|
225
|
+
unit.source?.ref && out("dim", unit.source.ref)
|
|
226
|
+
]);
|
|
227
|
+
const current = unit.rows.find((row) => !row.checked);
|
|
228
|
+
return [
|
|
229
|
+
` ${header}`,
|
|
230
|
+
...unit.rows.map((row) => ` ${renderRow(row, current?.number, out)}`)
|
|
231
|
+
];
|
|
232
|
+
}
|
|
201
233
|
function libraryLabel(library) {
|
|
202
234
|
const home = os.homedir();
|
|
203
235
|
const location = home && library.path.startsWith(`${home}/`) ? `~${library.path.slice(home.length)}` : library.path;
|
package/dist/task-view.js
CHANGED
|
@@ -7,8 +7,12 @@ import {
|
|
|
7
7
|
deriveChecklistStepDurations,
|
|
8
8
|
enumerateChecklistCheckboxes,
|
|
9
9
|
INTERACTIVE_CHECKLIST_MARKDOWN,
|
|
10
|
+
isSettledSubtaskStatus,
|
|
10
11
|
isTerminalWorkerSignalStatus,
|
|
11
12
|
READINESS_RECORD,
|
|
13
|
+
SUBTASK_INDEX_FILE,
|
|
14
|
+
SUBTASKS_DIR,
|
|
15
|
+
subtaskPaths,
|
|
12
16
|
WORKER_SIGNAL_FILE
|
|
13
17
|
} from "@farmslot/protocol";
|
|
14
18
|
import { readCommandJournal } from "./command-journal.js";
|
|
@@ -28,7 +32,9 @@ function collectTaskView(target, taskDir, now) {
|
|
|
28
32
|
({ name, root }) => ({ ...name ? { name } : {}, path: root })
|
|
29
33
|
)
|
|
30
34
|
};
|
|
31
|
-
if (!taskDir)
|
|
35
|
+
if (!taskDir) {
|
|
36
|
+
return { ...base, checklist: [], subtasks: [], activity: { ongoing: false, reason: "no-task" } };
|
|
37
|
+
}
|
|
32
38
|
const handoff = readJsonObject(path.join(taskDir, HANDOFF_INPUT));
|
|
33
39
|
const fixtures = readJsonObject(path.join(taskDir, "artifacts", "prepare", "fixtures.json"));
|
|
34
40
|
const sandbox = readJsonObject(path.join(taskDir, "artifacts", READINESS_RECORD)) ?? readJsonObject(path.join(target, recipeRuntimeDir(), READINESS_RECORD));
|
|
@@ -81,6 +87,7 @@ function collectTaskView(target, taskDir, now) {
|
|
|
81
87
|
}),
|
|
82
88
|
sandbox: section({ ready: pickBoolean(sandbox, "ready") }),
|
|
83
89
|
checklist,
|
|
90
|
+
subtasks: readSubtasks(taskDir),
|
|
84
91
|
signal: section({
|
|
85
92
|
status: pickString(signal, "status"),
|
|
86
93
|
step: pickString(signal, "step"),
|
|
@@ -167,6 +174,32 @@ function readChecklistRows(file) {
|
|
|
167
174
|
checked: item.checked
|
|
168
175
|
}));
|
|
169
176
|
}
|
|
177
|
+
function readSubtasks(taskDir) {
|
|
178
|
+
const index = readJsonObject(path.join(taskDir, SUBTASKS_DIR, SUBTASK_INDEX_FILE));
|
|
179
|
+
if (!index) return [];
|
|
180
|
+
const units = index.units;
|
|
181
|
+
if (!Array.isArray(units)) return [];
|
|
182
|
+
const views = [];
|
|
183
|
+
for (const entry of units) {
|
|
184
|
+
const unit = asObject(entry);
|
|
185
|
+
const id = pickString(unit, "id");
|
|
186
|
+
const parentStep = pickNumber(asObject(unit?.parent), "stepNumber");
|
|
187
|
+
if (id === void 0 || parentStep === void 0) continue;
|
|
188
|
+
const paths = subtaskPaths(id);
|
|
189
|
+
const signal = readJsonObject(path.join(taskDir, paths.signal));
|
|
190
|
+
const status = pickString(signal, "status");
|
|
191
|
+
const source = asObject(unit?.source);
|
|
192
|
+
views.push({
|
|
193
|
+
id,
|
|
194
|
+
parentStep,
|
|
195
|
+
...status !== void 0 ? { status } : {},
|
|
196
|
+
settled: isSettledSubtaskStatus(status),
|
|
197
|
+
rows: readChecklistRows(path.join(taskDir, paths.checklist)),
|
|
198
|
+
...section({ kind: pickString(source, "kind"), ref: pickString(source, "ref") }) ? { source: section({ kind: pickString(source, "kind"), ref: pickString(source, "ref") }) } : {}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return views;
|
|
202
|
+
}
|
|
170
203
|
function trimLabel(text) {
|
|
171
204
|
const plain = checklistStepName(text).replace(/\*\*/gu, "").replace(/^\d+\.\s*/u, "").replace(/\s+/gu, " ").trim();
|
|
172
205
|
if (plain.length <= CHECKLIST_LABEL_LIMIT) return plain;
|
package/docs/RECIPES.md
CHANGED
|
@@ -488,3 +488,12 @@ mm-harness run --list --json
|
|
|
488
488
|
```
|
|
489
489
|
|
|
490
490
|
See [Security](SECURITY.md) before enabling custom executable actions.
|
|
491
|
+
|
|
492
|
+
### Packaging parameterized runs
|
|
493
|
+
|
|
494
|
+
New runs retain `recipe-invocation.json` alongside their original recipe and
|
|
495
|
+
resolved dependencies. The canonical validator reads those execution-bound
|
|
496
|
+
inputs when validating the artifact directory, so Pretty PR does not need to
|
|
497
|
+
reconstruct parameters from logs or a later plan. Keep the invocation artifact
|
|
498
|
+
and its manifest entry when copying a run. Redacted credential inputs cannot be
|
|
499
|
+
revalidated; pass fixture references instead of credentials.
|
|
@@ -927,7 +927,7 @@ export async function simulatorScreenshot(input, relPath) {
|
|
|
927
927
|
path: relative,
|
|
928
928
|
type: 'screenshot',
|
|
929
929
|
nodeId: input.context.nodeId,
|
|
930
|
-
label: input.node?.description || `${input.action} screenshot`,
|
|
930
|
+
label: input.node?.label || input.node?.description || `${input.action} screenshot`,
|
|
931
931
|
category: 'evidence',
|
|
932
932
|
mimeType: 'image/png',
|
|
933
933
|
metadata: {
|
|
@@ -976,7 +976,7 @@ async function androidScreenshot(input, relPath) {
|
|
|
976
976
|
path: relative,
|
|
977
977
|
type: 'screenshot',
|
|
978
978
|
nodeId: input.context.nodeId,
|
|
979
|
-
label: input.node?.description || `${input.action} screenshot`,
|
|
979
|
+
label: input.node?.label || input.node?.description || `${input.action} screenshot`,
|
|
980
980
|
category: 'evidence',
|
|
981
981
|
mimeType: 'image/png',
|
|
982
982
|
metadata: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@farmslot/agent-runtime": "0.
|
|
24
|
-
"@farmslot/expo-recipe": "0.
|
|
23
|
+
"@farmslot/agent-runtime": "0.13.0",
|
|
24
|
+
"@farmslot/expo-recipe": "0.11.0",
|
|
25
25
|
"@farmslot/handoff": "^0.3.1",
|
|
26
|
-
"@farmslot/protocol": "0.
|
|
27
|
-
"@farmslot/recipe-harness": "0.
|
|
26
|
+
"@farmslot/protocol": "0.30.0",
|
|
27
|
+
"@farmslot/recipe-harness": "0.18.1",
|
|
28
28
|
"agent-device": "0.19.3",
|
|
29
29
|
"commander": "^12.0.0",
|
|
30
30
|
"es-module-lexer": "2.3.1",
|