@deeeed/metamask-harness 0.92.12 → 0.92.15
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 +12 -0
- package/adapters/extension/inject.mjs +1 -0
- package/adapters/extension/stop-viewers.sh +23 -2
- package/adapters/manifest.json +5 -0
- package/adapters/mobile/lib/idb-companion.sh +7 -22
- package/adapters/shared/process-identity.sh +35 -0
- package/dist/adapters/extension/surface.js +22 -3
- package/dist/command-contract.js +15 -0
- package/dist/commands/stop.js +31 -0
- package/dist/mm-harness-cli.js +3 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
- Refresh Terminal contract goldens for the shipped console-error window, capture-provider metadata and engine evidence inventories.
|
|
8
8
|
|
|
9
|
+
## 0.92.15 - 2026-10-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Add Extension viewer-only teardown for browser reservations, retaining the browser and webpack while verifying console collectors exit and reporting cleanup failures.
|
|
14
|
+
|
|
15
|
+
## 0.92.14 - 2026-10-16
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Pin the worker dependency set to recipe-runner 0.38.1 so nested recipe nodes produce safe still filenames and publication preserves retained evidence links.
|
|
20
|
+
|
|
9
21
|
## 0.92.12 - 2026-10-16
|
|
10
22
|
|
|
11
23
|
### Fixed
|
|
@@ -127,6 +127,7 @@ copyFile(path.join(runnerDir, 'adapters/shared/path-defaults.json'), path.join(h
|
|
|
127
127
|
copyFile(path.join(runnerDir, 'adapters/shared/json-field.sh'), path.join(harnessDir, 'scripts/lib/json-field.sh'));
|
|
128
128
|
copyFile(path.join(runnerDir, 'adapters/shared/tmux-session.sh'), path.join(harnessDir, 'scripts/lib/tmux-session.sh'));
|
|
129
129
|
copyFile(path.join(runnerDir, 'adapters/shared/tmux-viewer.sh'), path.join(harnessDir, 'scripts/lib/tmux-viewer.sh'));
|
|
130
|
+
copyFile(path.join(runnerDir, 'adapters/shared/process-identity.sh'), path.join(harnessDir, 'scripts/lib/process-identity.sh'));
|
|
130
131
|
copyFile(path.join(runnerDir, 'adapters/shared/log-tui.mjs'), path.join(harnessDir, 'scripts/lib/log-tui.mjs'));
|
|
131
132
|
copyAdapterWeb(path.join(harnessDir, 'scripts/node_modules/@farmslot/adapter-web'));
|
|
132
133
|
copyFile(path.join(runnerDir, 'adapters/extension/lib/product-config.cjs'), path.join(harnessDir, 'scripts/lib/product-config.cjs'));
|
|
@@ -28,6 +28,18 @@ for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-view
|
|
|
28
28
|
done
|
|
29
29
|
unset _tv_src
|
|
30
30
|
|
|
31
|
+
_process_lib="$SCRIPT_DIR/lib/process-identity.sh"
|
|
32
|
+
[ -f "$_process_lib" ] || _process_lib="$SCRIPT_DIR/../shared/process-identity.sh"
|
|
33
|
+
# shellcheck disable=SC1090
|
|
34
|
+
. "$_process_lib"
|
|
35
|
+
unset _process_lib
|
|
36
|
+
|
|
37
|
+
_console_collector_owned() {
|
|
38
|
+
[ "$(process_identity "$1")" = "$2" ] &&
|
|
39
|
+
[ "$(ps -ww -p "$1" -o command= 2>/dev/null | sed 's/^[[:space:]]*//')" = "$3" ] &&
|
|
40
|
+
[ "$(process_identity "$1")" = "$2" ]
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
runtime_dir="$TARGET/$(recipe_runtime_dir)"
|
|
32
44
|
set +e
|
|
33
45
|
|
|
@@ -40,16 +52,25 @@ if command -v tmux_viewer_sweep_prefix >/dev/null 2>&1; then
|
|
|
40
52
|
tmux_viewer_sweep_prefix "console" "$runtime_dir"
|
|
41
53
|
fi
|
|
42
54
|
|
|
43
|
-
ps -axo pid=,command= 2>/dev/null | while read -r pid command; do
|
|
55
|
+
ps -ww -axo pid=,command= 2>/dev/null | while read -r pid command; do
|
|
44
56
|
[ -n "${pid:-}" ] || continue
|
|
45
57
|
case "$command" in
|
|
46
58
|
*console-tail.mjs*) ;;
|
|
47
59
|
*) continue ;;
|
|
48
60
|
esac
|
|
49
61
|
case "$command" in
|
|
50
|
-
*"$runtime_dir/"*)
|
|
62
|
+
*"$runtime_dir/"*)
|
|
63
|
+
identity="$(process_identity "$pid")"
|
|
64
|
+
if [ -z "$identity" ] && ! kill -0 "$pid" 2>/dev/null; then continue; fi
|
|
65
|
+
stop_verified_process "$pid" "$identity" TERM 0.05 _console_collector_owned "$pid" "$identity" "$command" || exit 1
|
|
66
|
+
;;
|
|
51
67
|
esac
|
|
52
68
|
done
|
|
69
|
+
stop_status=$?
|
|
70
|
+
if [ "$stop_status" -ne 0 ]; then
|
|
71
|
+
echo "Console collector stop was not confirmed. Next: mm-harness status --target \"$TARGET\" --json" >&2
|
|
72
|
+
exit "$stop_status"
|
|
73
|
+
fi
|
|
53
74
|
|
|
54
75
|
rm -f "$runtime_dir/console.tmux" "$runtime_dir/webpack.tmux" "$runtime_dir/console-tail.pid"
|
|
55
76
|
exit 0
|
package/adapters/manifest.json
CHANGED
|
@@ -527,6 +527,11 @@
|
|
|
527
527
|
"entry": "adapters/shared/reap-checkout-metros.sh",
|
|
528
528
|
"kind": "lib"
|
|
529
529
|
},
|
|
530
|
+
{
|
|
531
|
+
"id": "lib/process-identity",
|
|
532
|
+
"entry": "adapters/shared/process-identity.sh",
|
|
533
|
+
"kind": "lib"
|
|
534
|
+
},
|
|
530
535
|
{
|
|
531
536
|
"id": "lib/sync-wallet-fixture",
|
|
532
537
|
"entry": "adapters/shared/sync-wallet-fixture.sh",
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Stop only companions bound to this checkout and its selected simulator.
|
|
3
3
|
|
|
4
|
+
# shellcheck disable=SC1091
|
|
5
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")/../../shared" && pwd)/process-identity.sh"
|
|
6
|
+
|
|
4
7
|
_idb_companion_identity() {
|
|
5
|
-
|
|
8
|
+
process_identity "$1"
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
# 1 is foreign; 2 is unknown and must not authorize a signal.
|
|
@@ -21,7 +24,7 @@ _idb_companion_owned() {
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
stop_checkout_idb_companions() {
|
|
24
|
-
local repo="$1" udid="${2:-}" pids pid identity status
|
|
27
|
+
local repo="$1" udid="${2:-}" pids pid identity status
|
|
25
28
|
if pids="$(pgrep -u "$(id -u)" -x idb_companion 2>/dev/null)"; then :
|
|
26
29
|
else
|
|
27
30
|
status=$?
|
|
@@ -41,26 +44,8 @@ stop_checkout_idb_companions() {
|
|
|
41
44
|
printf 'Companion ownership is unknown. Next: mm-harness status --target "%s"\n' "$repo" >&2
|
|
42
45
|
return 1
|
|
43
46
|
fi
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if ! _idb_companion_owned "$repo" "$udid" "$pid" "$identity"; then
|
|
47
|
-
printf 'Companion ownership changed. Next: mm-harness status --target "%s"\n' "$repo" >&2
|
|
48
|
-
return 1
|
|
49
|
-
fi
|
|
50
|
-
kill -"$signal" "$pid" 2>/dev/null || true
|
|
51
|
-
for attempt in $(seq 1 20); do
|
|
52
|
-
current="$(_idb_companion_identity "$pid")"
|
|
53
|
-
if [ -z "$current" ] && ! kill -0 "$pid" 2>/dev/null; then stopped=true; break; fi
|
|
54
|
-
if [ "$current" != "$identity" ]; then
|
|
55
|
-
printf 'Companion identity changed during stop. Next: mm-harness status --target "%s"\n' "$repo" >&2
|
|
56
|
-
return 1
|
|
57
|
-
fi
|
|
58
|
-
sleep 0.1
|
|
59
|
-
done
|
|
60
|
-
[ "$stopped" = true ] && break
|
|
61
|
-
done
|
|
62
|
-
if [ "$stopped" != true ]; then
|
|
63
|
-
printf 'Companion %s did not stop. Next: mm-harness status --target "%s"\n' "$pid" "$repo" >&2
|
|
47
|
+
if ! stop_verified_process "$pid" "$identity" 'TERM KILL' 0.1 _idb_companion_owned "$repo" "$udid" "$pid" "$identity"; then
|
|
48
|
+
printf 'Companion %s cleanup was not confirmed. Next: mm-harness status --target "%s"\n' "$pid" "$repo" >&2
|
|
64
49
|
return 1
|
|
65
50
|
fi
|
|
66
51
|
printf 'Stopped checkout idb companion %s\n' "$pid" >&2
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Confirm process identity and caller-owned scope before signalling.
|
|
3
|
+
|
|
4
|
+
process_identity() {
|
|
5
|
+
LC_ALL=C ps -p "$1" -o pid=,pgid=,lstart=,comm= 2>/dev/null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
stop_verified_process() {
|
|
9
|
+
local pid="$1" identity="$2" signals="$3" interval="$4" signal attempt current
|
|
10
|
+
shift 4
|
|
11
|
+
if [ -z "$identity" ]; then
|
|
12
|
+
if ! kill -0 "$pid" 2>/dev/null; then return 0; fi
|
|
13
|
+
printf 'Process %s identity is unknown; stop refused.\n' "$pid" >&2
|
|
14
|
+
return 1
|
|
15
|
+
fi
|
|
16
|
+
for signal in $signals; do
|
|
17
|
+
if ! "$@"; then
|
|
18
|
+
if [ -z "$(process_identity "$pid")" ] && ! kill -0 "$pid" 2>/dev/null; then return 0; fi
|
|
19
|
+
printf 'Process %s ownership changed before stop.\n' "$pid" >&2
|
|
20
|
+
return 1
|
|
21
|
+
fi
|
|
22
|
+
kill -"$signal" "$pid" 2>/dev/null || true
|
|
23
|
+
for attempt in $(seq 1 20); do
|
|
24
|
+
current="$(process_identity "$pid")"
|
|
25
|
+
if [ -z "$current" ] && ! kill -0 "$pid" 2>/dev/null; then return 0; fi
|
|
26
|
+
if [ "$current" != "$identity" ]; then
|
|
27
|
+
printf 'Process %s identity changed during stop.\n' "$pid" >&2
|
|
28
|
+
return 1
|
|
29
|
+
fi
|
|
30
|
+
sleep "$interval"
|
|
31
|
+
done
|
|
32
|
+
done
|
|
33
|
+
printf 'Process %s did not stop.\n' "$pid" >&2
|
|
34
|
+
return 1
|
|
35
|
+
}
|
|
@@ -21,6 +21,16 @@ function watcherStatus(buildLog) {
|
|
|
21
21
|
if (buildLog === "no-watch") return "down";
|
|
22
22
|
return buildLog;
|
|
23
23
|
}
|
|
24
|
+
function stopExtensionViewers(target) {
|
|
25
|
+
const result = spawnSync("bash", [path.join(runnerDir, "adapters/extension/stop-viewers.sh"), "--target", target], {
|
|
26
|
+
encoding: "utf8",
|
|
27
|
+
timeout: 5e3
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
status: result.error ? 1 : result.status ?? 1,
|
|
31
|
+
detail: result.error?.message || result.stderr.trim() || result.stdout.trim()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
24
34
|
function extensionRuntimeNextAction(reasonCode, target) {
|
|
25
35
|
const resolved = shellQuote(path.resolve(target));
|
|
26
36
|
if (reasonCode === "rpc-execution-failed") return `mm-harness logs --target ${resolved} --full`;
|
|
@@ -85,8 +95,16 @@ const extensionSurface = defineAdapter({
|
|
|
85
95
|
describe: () => "Extension runtime",
|
|
86
96
|
stop(target) {
|
|
87
97
|
const stopped = stopExtensionRuntime(target);
|
|
88
|
-
const
|
|
89
|
-
|
|
98
|
+
const viewers = stopExtensionViewers(target);
|
|
99
|
+
if (viewers.status !== 0) {
|
|
100
|
+
return {
|
|
101
|
+
kind: "stopped",
|
|
102
|
+
status: viewers.status,
|
|
103
|
+
signalled: stopped.signalled,
|
|
104
|
+
summary: `failed to stop Extension viewers for ${target}`,
|
|
105
|
+
output: viewers.detail
|
|
106
|
+
};
|
|
107
|
+
}
|
|
90
108
|
const summary = stopped.signalled > 0 || stopped.removedMarkers > 0 ? `stopped Extension runtime (${stopped.signalled} process${stopped.signalled === 1 ? "" : "es"}, ${stopped.removedMarkers} stale marker${stopped.removedMarkers === 1 ? "" : "s"}) for ${target}` : `Extension runtime not running for ${target} \u2014 nothing to stop`;
|
|
91
109
|
return { kind: "stopped", status: 0, summary, signalled: stopped.signalled };
|
|
92
110
|
},
|
|
@@ -154,5 +172,6 @@ const extensionSurface = defineAdapter({
|
|
|
154
172
|
adapterRegistry.register(extensionSurface);
|
|
155
173
|
export {
|
|
156
174
|
extensionRuntimeNextAction,
|
|
157
|
-
extensionSurface
|
|
175
|
+
extensionSurface,
|
|
176
|
+
stopExtensionViewers
|
|
158
177
|
};
|
package/dist/command-contract.js
CHANGED
|
@@ -696,6 +696,21 @@ Example:
|
|
|
696
696
|
})
|
|
697
697
|
}
|
|
698
698
|
},
|
|
699
|
+
{
|
|
700
|
+
name: "stop-viewers",
|
|
701
|
+
summary: "Stop checkout console collectors and log viewers while retaining the Extension browser and webpack.",
|
|
702
|
+
example: "mm-harness stop-viewers --adapter extension",
|
|
703
|
+
helpText: `mm-harness stop-viewers [flags]
|
|
704
|
+
|
|
705
|
+
Stop this checkout's Extension console collectors and log viewers.
|
|
706
|
+
The browser, profile and development server stay running. Used when an
|
|
707
|
+
Extension farm releases its browser reservation.
|
|
708
|
+
|
|
709
|
+
--adapter extension Target adapter
|
|
710
|
+
--target <path> Checkout path (default: cwd)
|
|
711
|
+
--json Machine-readable output`,
|
|
712
|
+
contract: { options: options(HELP, JSON, TARGET, ADAPTER) }
|
|
713
|
+
},
|
|
699
714
|
{
|
|
700
715
|
name: "call",
|
|
701
716
|
summary: "Run one action in isolation as a one-node recipe through the real engine path (fuzzy short names; key=value inputs; same trace/evidence as run).",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { optionFlag, parseArgs, resolveAdapter, shellQuote, usageOut } from "@farmslot/recipe-cli/harness";
|
|
2
|
+
import { stopExtensionViewers } from "../adapters/extension/surface.js";
|
|
3
|
+
async function handleStopViewers(argv) {
|
|
4
|
+
const parsed = parseArgs(argv);
|
|
5
|
+
const json = optionFlag(parsed.options, "json");
|
|
6
|
+
const { adapter, target } = resolveAdapter(parsed.options);
|
|
7
|
+
if (adapter !== "extension") {
|
|
8
|
+
return usageOut(json, "stop-viewers", "Viewer-only cleanup requires the Extension adapter.", "mm-harness stop-viewers --adapter extension");
|
|
9
|
+
}
|
|
10
|
+
const result = stopExtensionViewers(target);
|
|
11
|
+
const exitCode = result.status;
|
|
12
|
+
const detail = result.detail;
|
|
13
|
+
const userAction = `mm-harness status --target ${shellQuote(target)} --json`;
|
|
14
|
+
if (json) {
|
|
15
|
+
console.log(JSON.stringify({
|
|
16
|
+
schemaVersion: 1,
|
|
17
|
+
command: "stop-viewers",
|
|
18
|
+
adapter,
|
|
19
|
+
target,
|
|
20
|
+
status: exitCode === 0 ? "pass" : "fail",
|
|
21
|
+
exitCode,
|
|
22
|
+
...exitCode === 0 ? {} : { error: { code: "VIEWER_STOP_FAILED", message: detail || "Checkout viewers did not stop.", userAction } }
|
|
23
|
+
}));
|
|
24
|
+
} else {
|
|
25
|
+
console.error(exitCode === 0 ? `Stopped checkout viewers for ${target}; browser and watcher retained.` : `${detail || "Checkout viewers did not stop."} Next: ${userAction}`);
|
|
26
|
+
}
|
|
27
|
+
return exitCode;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
handleStopViewers
|
|
31
|
+
};
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
parseArgs,
|
|
21
21
|
targetPath
|
|
22
22
|
} from "@farmslot/recipe-cli/harness";
|
|
23
|
+
import { handleStopViewers } from "./commands/stop.js";
|
|
23
24
|
import { stopOptions } from "./provider-lifecycle.js";
|
|
24
25
|
import { adoptPluginAdapter, afterAdapterLoad, configuredLibrarySources } from "./adapters/plugin-surface.js";
|
|
25
26
|
import { withMainnetOrderAbort } from "./adapters/perps-mainnet-cleanup.js";
|
|
@@ -150,6 +151,7 @@ const PUBLIC_RUN = {
|
|
|
150
151
|
"execution-template": { run: handleExecutionTemplate },
|
|
151
152
|
actions: { run: (args) => handleActions(parseArgs(args), { catalog: metaMaskEngine }) },
|
|
152
153
|
stop: { run: (args) => handleStop(args, stopOptions) },
|
|
154
|
+
"stop-viewers": { run: handleStopViewers },
|
|
153
155
|
// A signal during run or call still cancels the run's mainnet orders.
|
|
154
156
|
call: { run: (args) => withMainnetOrderAbort(targetPath(parseArgs(args).options), () => handleCall(args, callOptions)) },
|
|
155
157
|
run: { run: (args) => withMainnetOrderAbort(targetPath(parseArgs(args).options), () => handleRun(args, runOptions)) },
|
|
@@ -188,7 +190,7 @@ const HELP_GROUPS = [
|
|
|
188
190
|
{
|
|
189
191
|
title: "DAILY LOOP",
|
|
190
192
|
blurb: "what a teammate runs many times a day (auto-ensures the overlay; --heal owns recovery)",
|
|
191
|
-
commands: ["status", "task", "prepare", "launch", "stop", "logs", "debug", "reload", "fixtures"]
|
|
193
|
+
commands: ["status", "task", "prepare", "launch", "stop", "stop-viewers", "logs", "debug", "reload", "fixtures"]
|
|
192
194
|
},
|
|
193
195
|
{
|
|
194
196
|
title: "DISCOVER",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@farmslot/adapter-node": "0.1.
|
|
25
|
-
"@farmslot/adapter-rn": "0.22.
|
|
26
|
-
"@farmslot/adapter-sdk": "0.
|
|
27
|
-
"@farmslot/adapter-web": "0.10.
|
|
28
|
-
"@farmslot/agent-runtime": "0.25.
|
|
24
|
+
"@farmslot/adapter-node": "0.1.30",
|
|
25
|
+
"@farmslot/adapter-rn": "0.22.19",
|
|
26
|
+
"@farmslot/adapter-sdk": "0.16.1",
|
|
27
|
+
"@farmslot/adapter-web": "0.10.10",
|
|
28
|
+
"@farmslot/agent-runtime": "0.25.4",
|
|
29
29
|
"@farmslot/handoff": "^0.3.1",
|
|
30
|
-
"@farmslot/protocol": "0.
|
|
31
|
-
"@farmslot/recipe-cli": "0.
|
|
32
|
-
"@farmslot/recipe-runner": "0.
|
|
30
|
+
"@farmslot/protocol": "0.50.0",
|
|
31
|
+
"@farmslot/recipe-cli": "0.22.2",
|
|
32
|
+
"@farmslot/recipe-runner": "0.38.1",
|
|
33
33
|
"agent-device": "0.19.3",
|
|
34
34
|
"commander": "^12.0.0",
|
|
35
35
|
"esbuild": "0.28.1",
|