@deeeed/metamask-harness 0.14.1 → 0.14.3
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 +13 -0
- package/adapters/core/cleanup.sh +0 -0
- package/adapters/core/inject.sh +0 -0
- package/adapters/extension/cleanup.mjs +0 -0
- package/adapters/extension/ensure-browser.sh +0 -0
- package/adapters/extension/inject.mjs +2 -0
- package/adapters/extension/launch-browser.cjs +26 -7
- package/adapters/extension/launch.sh +7 -9
- package/adapters/extension/live.sh +0 -0
- package/adapters/extension/readiness.mjs +0 -0
- package/adapters/extension/reattach.sh +0 -0
- package/adapters/extension/refresh-build.sh +0 -0
- package/adapters/extension/seed-fixture.sh +0 -0
- package/adapters/extension/sidepanel-toggle.sh +3 -3
- package/adapters/extension/snapshot-dist.sh +0 -0
- package/adapters/extension/start-watch.sh +5 -15
- package/adapters/extension/stop-viewers.sh +56 -0
- package/adapters/extension/verify.sh +0 -0
- package/adapters/extension/wallet-fixture-state.cjs +0 -0
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -0
- package/adapters/mobile/bridge-runtime/setup-wallet.sh +0 -0
- package/adapters/mobile/cleanup.sh +0 -0
- package/adapters/mobile/inject.sh +0 -0
- package/adapters/mobile/lib/metro-listener.sh +0 -0
- package/adapters/mobile/lib/tmux-viewer.sh +0 -0
- package/adapters/mobile/open-device.sh +0 -0
- package/adapters/mobile/prewarm-bundle.sh +0 -0
- package/adapters/mobile/start-metro.sh +0 -0
- package/adapters/mobile/stop-metro.sh +0 -0
- package/adapters/mobile/verify.sh +0 -0
- package/adapters/mobile/wait-for-bridge.sh +0 -0
- package/adapters/mobile/yarn-setup.sh +0 -0
- package/adapters/shared/activate-repo-node.sh +0 -0
- package/adapters/shared/activate-repo-ruby.sh +0 -0
- package/adapters/shared/cli-ux.sh +0 -0
- package/adapters/shared/ensure-runner-deps.sh +0 -0
- package/adapters/shared/harness-path.sh +0 -0
- package/adapters/shared/hash-helpers.sh +0 -0
- package/adapters/shared/json-field.sh +0 -0
- package/adapters/shared/open-log-window.sh +0 -0
- package/adapters/shared/reap-checkout-metros.sh +0 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +0 -0
- package/adapters/shared/resolve-farmslot-ports.sh +0 -0
- package/adapters/shared/resolve-slot-ports.mjs +0 -0
- package/adapters/shared/resolve-slot-ports.sh +0 -0
- package/adapters/shared/sync-wallet-fixture.sh +0 -0
- package/adapters/shared/tmux-session.sh +0 -0
- package/adapters/shared/tmux-viewer.sh +75 -0
- package/dist/adapters/extension/runtime-decision.js +3 -3
- package/dist/adapters/extension/surface.js +4 -4
- package/dist/commands/status.js +31 -1
- package/package.json +5 -5
- package/scripts/completions.sh +0 -0
- package/scripts/install-completions.sh +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.14.3 - 2026-07-09
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Extension `status` and `doctor` now accept a sidepanel-only live runtime, matching `run`/`call` health checks instead of requiring a fullscreen `home.html` tab after `launch --sidepanel`.
|
|
9
|
+
- Extension sidepanel launch waits longer for Chrome to expose the `sidepanel.html` CDP target, avoiding false failures after Chrome accepts the open request.
|
|
10
|
+
- Extension tmux viewers are opened only in the run-owned tmux session and are cleaned by `mm-harness stop`, preventing stale `webpack-*`/`console-*` windows from leaking across slots.
|
|
11
|
+
- Extension clean relaunch recognizes prior same-checkout Chrome instances loaded from both timestamped harness snapshots and `temp/recipe/runtime/runtime-dist`, while still refusing foreign CDP owners.
|
|
12
|
+
|
|
13
|
+
## 0.14.2 - 2026-07-09
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Require `@farmslot/recipe-harness` `^0.4.3` so runtime dependency readiness trusts install markers newer than an old recorded baseline and stops repeatedly prompting managed slots to reinstall fresh dependencies.
|
|
17
|
+
|
|
5
18
|
## 0.14.1 - 2026-07-09
|
|
6
19
|
|
|
7
20
|
### Added
|
package/adapters/core/cleanup.sh
CHANGED
|
File without changes
|
package/adapters/core/inject.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -104,6 +104,7 @@ copyFile(path.join(runnerDir, 'adapters/extension/console-tail.mjs'), path.join(
|
|
|
104
104
|
copyFile(path.join(runnerDir, 'adapters/extension/launch.sh'), path.join(harnessDir, 'scripts/launch.sh'));
|
|
105
105
|
copyFile(path.join(runnerDir, 'adapters/extension/live.sh'), path.join(harnessDir, 'scripts/live.sh'));
|
|
106
106
|
copyFile(path.join(runnerDir, 'adapters/extension/start-watch.sh'), path.join(harnessDir, 'scripts/start-watch.sh'));
|
|
107
|
+
copyFile(path.join(runnerDir, 'adapters/extension/stop-viewers.sh'), path.join(harnessDir, 'scripts/stop-viewers.sh'));
|
|
107
108
|
copyFile(path.join(runnerDir, 'adapters/extension/snapshot-dist.sh'), path.join(harnessDir, 'scripts/snapshot-dist.sh'));
|
|
108
109
|
copyFile(path.join(runnerDir, 'adapters/extension/pin-remote-flags.cjs'), path.join(harnessDir, 'scripts/pin-remote-flags.cjs'));
|
|
109
110
|
copyFile(path.join(runnerDir, 'adapters/extension/seed-fixture.sh'), path.join(harnessDir, 'scripts/seed-fixture.sh'));
|
|
@@ -126,6 +127,7 @@ copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports-core.mjs')
|
|
|
126
127
|
copyFile(path.join(runnerDir, 'adapters/shared/path-defaults.json'), path.join(harnessDir, 'scripts/lib/path-defaults.json'));
|
|
127
128
|
copyFile(path.join(runnerDir, 'adapters/shared/json-field.sh'), path.join(harnessDir, 'scripts/lib/json-field.sh'));
|
|
128
129
|
copyFile(path.join(runnerDir, 'adapters/shared/tmux-session.sh'), path.join(harnessDir, 'scripts/lib/tmux-session.sh'));
|
|
130
|
+
copyFile(path.join(runnerDir, 'adapters/shared/tmux-viewer.sh'), path.join(harnessDir, 'scripts/lib/tmux-viewer.sh'));
|
|
129
131
|
copyFile(path.join(runnerDir, 'adapters/shared/log-tui.mjs'), path.join(harnessDir, 'scripts/lib/log-tui.mjs'));
|
|
130
132
|
copyFile(path.join(runnerDir, 'adapters/extension/lib/extension-id.cjs'), path.join(harnessDir, 'scripts/lib/extension-id.cjs'));
|
|
131
133
|
makeExecutableTree(path.join(harnessDir, 'scripts'));
|
|
@@ -64,7 +64,7 @@ fs.mkdirSync(path.dirname(args['chrome-pid']), { recursive: true });
|
|
|
64
64
|
// else is a browser this harness did not launch (possibly the user's personal
|
|
65
65
|
// Chrome), so we refuse rather than kill it or attach to it.
|
|
66
66
|
const listenerPids = cdpListenerPids(cdpPort);
|
|
67
|
-
const foreignPids = listenerPids.filter((pid) => !
|
|
67
|
+
const foreignPids = listenerPids.filter((pid) => !processIsOwnedHarnessBrowser(pid, args.profile, args['extension-dir']));
|
|
68
68
|
if (foreignPids.length > 0) {
|
|
69
69
|
throw new Error(
|
|
70
70
|
`Refusing to launch on CDP port ${cdpPort}: it is held by a browser this harness did not launch (pid ${foreignPids.join(', ')}). ` +
|
|
@@ -73,7 +73,7 @@ if (foreignPids.length > 0) {
|
|
|
73
73
|
}
|
|
74
74
|
const ownedPids = new Set(listenerPids);
|
|
75
75
|
const previousPid = readPidFile(args['chrome-pid']);
|
|
76
|
-
if (previousPid !== null &&
|
|
76
|
+
if (previousPid !== null && processIsOwnedHarnessBrowser(previousPid, args.profile, args['extension-dir'])) ownedPids.add(previousPid);
|
|
77
77
|
terminatePids([...ownedPids]);
|
|
78
78
|
if (args['reset-profile'] !== undefined) {
|
|
79
79
|
fs.rmSync(args.profile, { recursive: true, force: true });
|
|
@@ -164,16 +164,35 @@ function assertIsolatedProfile(profile) {
|
|
|
164
164
|
// Proof of ownership: a process is ours iff its command line loads the exact
|
|
165
165
|
// --user-data-dir we launch with. If the process can't be inspected we treat it
|
|
166
166
|
// as NOT ours (fail safe — never assume ownership of an opaque process).
|
|
167
|
-
function
|
|
168
|
-
|
|
167
|
+
function processIsOwnedHarnessBrowser(pid, profile, extensionDir) {
|
|
168
|
+
const command = processCommand(pid);
|
|
169
|
+
if (!command) return false;
|
|
170
|
+
if (command.includes(`--user-data-dir=${profile}`)) return true;
|
|
171
|
+
const ownerRoot = harnessOwnerRootFromExtensionDir(extensionDir);
|
|
172
|
+
return Boolean(ownerRoot && command.includes(`--load-extension=${ownerRoot}`));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function harnessOwnerRootFromExtensionDir(extensionDir) {
|
|
176
|
+
const resolved = path.resolve(extensionDir);
|
|
177
|
+
for (const marker of [
|
|
178
|
+
`${path.sep}temp${path.sep}recipe${path.sep}harness${path.sep}extension${path.sep}live${path.sep}`,
|
|
179
|
+
`${path.sep}temp${path.sep}recipe${path.sep}runtime${path.sep}runtime-dist`,
|
|
180
|
+
]) {
|
|
181
|
+
const markerIndex = resolved.indexOf(marker);
|
|
182
|
+
if (markerIndex !== -1) return resolved.slice(0, markerIndex + marker.length);
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function processCommand(pid) {
|
|
188
|
+
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
169
189
|
try {
|
|
170
|
-
|
|
190
|
+
return execFileSync('ps', ['-ww', '-o', 'command=', '-p', String(pid)], {
|
|
171
191
|
encoding: 'utf8',
|
|
172
192
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
173
193
|
});
|
|
174
|
-
return command.includes(`--user-data-dir=${profile}`);
|
|
175
194
|
} catch {
|
|
176
|
-
return
|
|
195
|
+
return null;
|
|
177
196
|
}
|
|
178
197
|
}
|
|
179
198
|
|
|
@@ -119,21 +119,19 @@ fi
|
|
|
119
119
|
# tabs. Skipped silently when there is no tmux session or CDP (headless/CI), so
|
|
120
120
|
# it never affects the launch result.
|
|
121
121
|
if [ "$status" = "pass" ] && [ -n "$CDP_PORT" ] && command -v tmux >/dev/null 2>&1; then
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-viewer.sh"; do
|
|
123
|
+
# shellcheck disable=SC1091
|
|
124
|
+
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
125
|
+
done
|
|
126
|
+
unset _tv_src
|
|
126
127
|
console_tail="$SCRIPT_DIR/console-tail.mjs"
|
|
127
|
-
if
|
|
128
|
+
if command -v tmux_viewer_open >/dev/null 2>&1 && [ -f "$console_tail" ]; then
|
|
128
129
|
console_runtime="$TARGET/$( (cd "$TARGET" && recipe_runtime_dir) 2>/dev/null || echo temp/recipe/runtime)"
|
|
129
130
|
mkdir -p "$console_runtime"
|
|
130
131
|
console_log="$console_runtime/extension-console.log"
|
|
131
132
|
: > "$console_log"
|
|
132
|
-
|
|
133
|
-
tmux kill-window -t "${console_session}:${console_window}" >/dev/null 2>&1 || true
|
|
134
|
-
tmux new-window -d -t "$console_session" -n "$console_window" \
|
|
133
|
+
tmux_viewer_open "console" "$CDP_PORT" "$console_log" "$console_runtime" "$console_runtime/console.tmux" \
|
|
135
134
|
"exec node $(printf '%q' "$console_tail") --cdp-port ${CDP_PORT} --log $(printf '%q' "$console_log")"
|
|
136
|
-
echo "[recipe-harness] extension console viewer in tmux window ${console_session}:${console_window}"
|
|
137
135
|
fi
|
|
138
136
|
fi
|
|
139
137
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# bash sidepanel-toggle.sh open --cdp-port 6663 [--ext-id <extension id>]
|
|
20
20
|
# bash sidepanel-toggle.sh close --cdp-port 6663
|
|
21
21
|
# bash sidepanel-toggle.sh toggle --cdp-port 6663
|
|
22
|
-
# bash sidepanel-toggle.sh cycle --cdp-port 6663 [--settle-ms
|
|
22
|
+
# bash sidepanel-toggle.sh cycle --cdp-port 6663 [--settle-ms 10000]
|
|
23
23
|
#
|
|
24
24
|
# CDP can inspect, activate, and close sidepanel targets. Opening the side panel
|
|
25
25
|
# is done from a clicked extension-page button so Chrome treats
|
|
@@ -66,7 +66,7 @@ RUNTIME_DIST_DIR="${RECIPE_RUNTIME_DIST_DIR:-runtime-dist}"
|
|
|
66
66
|
|
|
67
67
|
CDP_PORT="${CDP_PORT:-}"
|
|
68
68
|
EXT_ID="${EXT_ID:-}"
|
|
69
|
-
SETTLE_MS="${SETTLE_MS:-
|
|
69
|
+
SETTLE_MS="${SETTLE_MS:-10000}"
|
|
70
70
|
AGENT_DIR="${AGENT_DIR:-$SCRIPT_DIR}"
|
|
71
71
|
|
|
72
72
|
while [[ $# -gt 0 ]]; do
|
|
@@ -355,7 +355,7 @@ NODE
|
|
|
355
355
|
return 0
|
|
356
356
|
fi
|
|
357
357
|
|
|
358
|
-
echo "FAIL: sidepanel did not appear within ${SETTLE_MS}ms" >&2
|
|
358
|
+
echo "FAIL: sidepanel target did not appear within ${SETTLE_MS}ms after Chrome accepted the open request" >&2
|
|
359
359
|
echo " Current CDP targets:" >&2
|
|
360
360
|
print_targets >&2
|
|
361
361
|
exit 3
|
|
File without changes
|
|
@@ -237,22 +237,14 @@ printf '%s\n' "$started_pid" > "$watch_pid_file"
|
|
|
237
237
|
# The orchestrator names the session; the harness only populates windows. Resolve
|
|
238
238
|
# via the run-owned ladder (RECIPE_TMUX_SESSION → agentic-runtime.json session →
|
|
239
239
|
# current tmux client) — no slot-number derivation.
|
|
240
|
-
|
|
241
|
-
for _tv_src in "$SCRIPT_DIR/lib/tmux-session.sh" "$SCRIPT_DIR/../shared/tmux-session.sh"; do
|
|
240
|
+
for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-viewer.sh"; do
|
|
242
241
|
# shellcheck disable=SC1091
|
|
243
242
|
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
244
243
|
done
|
|
245
244
|
unset _tv_src
|
|
246
|
-
if command -v
|
|
247
|
-
tmux_session="$(resolve_run_tmux_session "$RUNTIME_DIR")"
|
|
248
|
-
fi
|
|
249
|
-
if [ -n "$tmux_session" ] && command -v tmux >/dev/null 2>&1 && tmux has-session -t "$tmux_session" 2>/dev/null; then
|
|
250
|
-
tmux_window="webpack-${WATCHER_PORT:-default}"
|
|
251
|
-
while tmux list-windows -t "$tmux_session" -F '#{window_name}' 2>/dev/null | grep -Fxq "$tmux_window"; do
|
|
252
|
-
tmux kill-window -t "${tmux_session}:${tmux_window}" 2>/dev/null || break
|
|
253
|
-
done
|
|
245
|
+
if command -v tmux_viewer_open >/dev/null 2>&1; then
|
|
254
246
|
log_q="$(printf '%q' "$watch_log")"
|
|
255
|
-
|
|
247
|
+
tmux_viewer_open "webpack" "${WATCHER_PORT:-default}" "$watch_log" "$RUNTIME_DIR" "$RUNTIME_DIR/webpack.tmux" "exec tail -n +1 -F $log_q"
|
|
256
248
|
fi
|
|
257
249
|
ln -sf "$(basename "$watch_log")" "$RUNTIME_DIR/webpack.log"
|
|
258
250
|
|
|
@@ -277,10 +269,8 @@ for i in {1..240}; do
|
|
|
277
269
|
compiled=true
|
|
278
270
|
break
|
|
279
271
|
fi
|
|
280
|
-
echo '[recipe-harness] compact build watcher exited before compile marker' >&2
|
|
281
|
-
|
|
282
|
-
rm -f "$watch_pid_file" "$slot_watch_pid_file"
|
|
283
|
-
exit 1
|
|
272
|
+
echo '[recipe-harness] compact build watcher exited before compile marker; continuing to poll webpack log' >&2
|
|
273
|
+
watch_tail_pid=""
|
|
284
274
|
fi
|
|
285
275
|
if ! write_canonical_watch_pid; then
|
|
286
276
|
if [ "$i" -le 10 ]; then
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stop-viewers.sh — close extension tmux viewers and console tail for one checkout.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
TARGET="$PWD"
|
|
6
|
+
while [ "$#" -gt 0 ]; do
|
|
7
|
+
case "$1" in
|
|
8
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
9
|
+
-h|--help) echo "Usage: stop-viewers.sh [--target <metamask-extension>]"; exit 0 ;;
|
|
10
|
+
*) echo "stop-viewers: unknown arg: $1" >&2; exit 2 ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
|
|
14
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
# shellcheck disable=SC1091
|
|
17
|
+
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../shared/harness-path.sh"; do
|
|
18
|
+
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
19
|
+
done
|
|
20
|
+
unset _hp
|
|
21
|
+
if ! command -v recipe_runtime_dir >/dev/null 2>&1; then
|
|
22
|
+
echo "stop-viewers: shared lib harness-path.sh not found; reinstall the runner." >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
# shellcheck disable=SC1091
|
|
26
|
+
for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-viewer.sh"; do
|
|
27
|
+
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
28
|
+
done
|
|
29
|
+
unset _tv_src
|
|
30
|
+
|
|
31
|
+
runtime_dir="$TARGET/$(recipe_runtime_dir)"
|
|
32
|
+
console_log="$runtime_dir/extension-console.log"
|
|
33
|
+
set +e
|
|
34
|
+
|
|
35
|
+
if command -v tmux_viewer_close_marker >/dev/null 2>&1; then
|
|
36
|
+
tmux_viewer_close_marker "$runtime_dir/webpack.tmux"
|
|
37
|
+
tmux_viewer_close_marker "$runtime_dir/console.tmux"
|
|
38
|
+
fi
|
|
39
|
+
if command -v tmux_viewer_sweep_prefix >/dev/null 2>&1; then
|
|
40
|
+
tmux_viewer_sweep_prefix "webpack" "$runtime_dir"
|
|
41
|
+
tmux_viewer_sweep_prefix "console" "$runtime_dir"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
ps -axo pid=,command= 2>/dev/null | while read -r pid command; do
|
|
45
|
+
[ -n "${pid:-}" ] || continue
|
|
46
|
+
case "$command" in
|
|
47
|
+
*console-tail.mjs*) ;;
|
|
48
|
+
*) continue ;;
|
|
49
|
+
esac
|
|
50
|
+
case "$command" in
|
|
51
|
+
*"--log $console_log"*|*"$console_log"*) kill "$pid" 2>/dev/null || true ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
rm -f "$runtime_dir/console.tmux" "$runtime_dir/webpack.tmux"
|
|
56
|
+
exit 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tmux-viewer.sh — run-owned tmux log viewer helpers.
|
|
3
|
+
#
|
|
4
|
+
# The orchestrator owns session names; the harness only opens windows inside a
|
|
5
|
+
# session resolved by tmux-session.sh. When no run-owned session is available the
|
|
6
|
+
# viewer is skipped. Runtime processes still run and write logs.
|
|
7
|
+
|
|
8
|
+
_tv_shared_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
# shellcheck disable=SC1091
|
|
10
|
+
for _tv_src in "$_tv_shared_dir/tmux-session.sh" "$_tv_shared_dir/lib/tmux-session.sh"; do
|
|
11
|
+
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
12
|
+
done
|
|
13
|
+
unset _tv_src _tv_shared_dir
|
|
14
|
+
|
|
15
|
+
tmux_viewer_port_is_listening() {
|
|
16
|
+
local port="${1:-}"
|
|
17
|
+
case "$port" in ''|*[!0-9]*) return 1 ;; esac
|
|
18
|
+
command -v lsof >/dev/null 2>&1 || return 1
|
|
19
|
+
lsof -nP -iTCP:"$port" -sTCP:LISTEN -t >/dev/null 2>&1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
tmux_viewer_open() {
|
|
23
|
+
local prefix="$1" port="$2" log="$3" runtime_dir="$4" marker_file="$5" command="$6"
|
|
24
|
+
command -v tmux >/dev/null 2>&1 || return 0
|
|
25
|
+
command -v resolve_run_tmux_session >/dev/null 2>&1 || return 0
|
|
26
|
+
|
|
27
|
+
local session window
|
|
28
|
+
session="$(resolve_run_tmux_session "$runtime_dir")"
|
|
29
|
+
{ [ -n "$session" ] && tmux has-session -t "=$session" 2>/dev/null; } || return 0
|
|
30
|
+
|
|
31
|
+
window="${prefix}-${port:-default}"
|
|
32
|
+
tmux list-windows -t "$session" -F '#{window_name}' 2>/dev/null | while IFS= read -r _win; do
|
|
33
|
+
case "$_win" in
|
|
34
|
+
"$prefix"-*) ;;
|
|
35
|
+
*) continue ;;
|
|
36
|
+
esac
|
|
37
|
+
_port="${_win#"$prefix"-}"
|
|
38
|
+
if [ "$_win" != "$window" ] || ! tmux_viewer_port_is_listening "$_port"; then
|
|
39
|
+
tmux kill-window -t "${session}:${_win}" >/dev/null 2>&1 || true
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
tmux kill-window -t "${session}:${window}" >/dev/null 2>&1 || true
|
|
44
|
+
tmux new-window -d -t "$session" -n "$window" "$command"
|
|
45
|
+
if [ -n "$marker_file" ]; then
|
|
46
|
+
mkdir -p "$(dirname "$marker_file")"
|
|
47
|
+
printf '%s:%s\n' "$session" "$window" > "$marker_file"
|
|
48
|
+
fi
|
|
49
|
+
printf '%s log viewer → tmux %s:%s\n' "$prefix" "$session" "$window" >&2
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
tmux_viewer_close_marker() {
|
|
53
|
+
local marker_file="$1"
|
|
54
|
+
command -v tmux >/dev/null 2>&1 || { rm -f "$marker_file"; return 0; }
|
|
55
|
+
if [ -f "$marker_file" ]; then
|
|
56
|
+
local target
|
|
57
|
+
target="$(cat "$marker_file" 2>/dev/null || true)"
|
|
58
|
+
[ -z "$target" ] || tmux kill-window -t "$target" >/dev/null 2>&1 || true
|
|
59
|
+
fi
|
|
60
|
+
rm -f "$marker_file"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
tmux_viewer_sweep_prefix() {
|
|
64
|
+
local prefix="$1" runtime_dir="$2"
|
|
65
|
+
command -v tmux >/dev/null 2>&1 || return 0
|
|
66
|
+
command -v resolve_run_tmux_session >/dev/null 2>&1 || return 0
|
|
67
|
+
local session
|
|
68
|
+
session="$(resolve_run_tmux_session "$runtime_dir")"
|
|
69
|
+
{ [ -n "$session" ] && tmux has-session -t "=$session" 2>/dev/null; } || return 0
|
|
70
|
+
tmux list-windows -t "$session" -F '#{window_name}' 2>/dev/null | while IFS= read -r _win; do
|
|
71
|
+
case "$_win" in
|
|
72
|
+
"$prefix"-*) tmux kill-window -t "${session}:${_win}" >/dev/null 2>&1 || true ;;
|
|
73
|
+
esac
|
|
74
|
+
done
|
|
75
|
+
}
|
|
@@ -184,11 +184,11 @@ function distCheck(target) {
|
|
|
184
184
|
function isExtensionDistStale(target) {
|
|
185
185
|
return distCheck(path.resolve(target)).status === "stale";
|
|
186
186
|
}
|
|
187
|
-
async function cdpCheck(target, cdpPort) {
|
|
187
|
+
async function cdpCheck(target, cdpPort, pageMode) {
|
|
188
188
|
if (!cdpPort) return { status: "skipped" };
|
|
189
189
|
try {
|
|
190
190
|
const { checkExtensionRuntimeHealth } = await import("./runtime.js");
|
|
191
|
-
const report = await checkExtensionRuntimeHealth(target, cdpPort);
|
|
191
|
+
const report = await checkExtensionRuntimeHealth(target, cdpPort, { pageMode });
|
|
192
192
|
return report.status === "PASS" ? { status: "pass" } : { status: "fail", findings: report.findings };
|
|
193
193
|
} catch (error) {
|
|
194
194
|
return { status: "fail", findings: [error instanceof Error ? error.message : String(error)] };
|
|
@@ -206,7 +206,7 @@ async function decideExtensionReadiness(target, options = {}) {
|
|
|
206
206
|
const webpackCache = webpackCacheCheck(resolved);
|
|
207
207
|
const buildLog = buildLogCheck(resolved, options.watchLog);
|
|
208
208
|
const dist = distCheck(resolved);
|
|
209
|
-
const cdp = await cdpCheck(resolved, options.cdpPort);
|
|
209
|
+
const cdp = await cdpCheck(resolved, options.cdpPort, options.pageMode);
|
|
210
210
|
const checks = { deps, webpackCache, buildLog, dist, cdp };
|
|
211
211
|
const install = [{ id: "yarn-install", argv: ["yarn", "install", "--immutable"], cwd: resolved }];
|
|
212
212
|
const relaunch = [{ id: "relaunch-browser" }];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { recipeRuntimePath } from "../../paths.js";
|
|
3
|
+
import { recipeRuntimePath, runnerDir } from "../../paths.js";
|
|
4
4
|
import { resolveExtensionSlotPorts, stopExtensionWatcher } from "../slot-ports.js";
|
|
5
5
|
import { decideExtensionReadiness } from "./runtime-decision.js";
|
|
6
6
|
function watcherStatus(buildLog) {
|
|
@@ -16,7 +16,7 @@ const extensionSurface = {
|
|
|
16
16
|
},
|
|
17
17
|
async runtimeStatus(target) {
|
|
18
18
|
const cdpPort = process.env.CDP_PORT ? parseInt(process.env.CDP_PORT, 10) : void 0;
|
|
19
|
-
const report = await decideExtensionReadiness(target, { cdpPort });
|
|
19
|
+
const report = await decideExtensionReadiness(target, { cdpPort, pageMode: "home-or-sidepanel" });
|
|
20
20
|
return {
|
|
21
21
|
decision: report.decision,
|
|
22
22
|
reasonCode: report.reasonCode,
|
|
@@ -47,8 +47,8 @@ const extensionSurface = {
|
|
|
47
47
|
describe: () => "webpack watcher",
|
|
48
48
|
stop(target) {
|
|
49
49
|
const signalled = stopExtensionWatcher(target);
|
|
50
|
-
const
|
|
51
|
-
spawnSync("
|
|
50
|
+
const stopViewers = path.join(runnerDir, "adapters/extension/stop-viewers.sh");
|
|
51
|
+
spawnSync("bash", [stopViewers, "--target", target], { stdio: "ignore", timeout: 5e3 });
|
|
52
52
|
const summary = signalled > 0 ? `stopped webpack watcher (${signalled} process${signalled === 1 ? "" : "es"}) for ${target}` : `webpack watcher not running for ${target} \u2014 nothing to stop`;
|
|
53
53
|
return { kind: "stopped", status: 0, summary, signalled };
|
|
54
54
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -58,8 +58,21 @@ async function handleStatus({ options }) {
|
|
|
58
58
|
)
|
|
59
59
|
);
|
|
60
60
|
} else {
|
|
61
|
+
const runtime = !fast && adapter === "extension" ? await surface.runtimeStatus(target) : void 0;
|
|
61
62
|
console.log(
|
|
62
|
-
JSON.stringify(
|
|
63
|
+
JSON.stringify(
|
|
64
|
+
{
|
|
65
|
+
schemaVersion: 1,
|
|
66
|
+
command: "status",
|
|
67
|
+
adapter,
|
|
68
|
+
target,
|
|
69
|
+
devices,
|
|
70
|
+
...runtime ? { runtime } : {},
|
|
71
|
+
next: runtime ? nextForRuntime(surface.hints.launch, surface.hints.relaunch, runtime) : next
|
|
72
|
+
},
|
|
73
|
+
null,
|
|
74
|
+
2
|
|
75
|
+
)
|
|
63
76
|
);
|
|
64
77
|
}
|
|
65
78
|
return EXIT.ok;
|
|
@@ -73,11 +86,28 @@ async function handleStatus({ options }) {
|
|
|
73
86
|
renderMobileLiveBlock(devices, liveView.liveMap, out);
|
|
74
87
|
renderAndroidPortHints(liveView.androidPortHints, out);
|
|
75
88
|
console.log(`${out("label", "Next:")} ${out("cmd", nextForLive(next, liveView.liveMap))}`);
|
|
89
|
+
} else if (!fast && adapter === "extension") {
|
|
90
|
+
const runtime = await surface.runtimeStatus(target);
|
|
91
|
+
renderRuntimeStatus(runtime, out);
|
|
92
|
+
console.log(`${out("label", "Next:")} ${out("cmd", nextForRuntime(surface.hints.launch, surface.hints.relaunch, runtime))}`);
|
|
76
93
|
} else {
|
|
77
94
|
console.log(`${out("label", "Next:")} ${out("cmd", next)}`);
|
|
78
95
|
}
|
|
79
96
|
return EXIT.ok;
|
|
80
97
|
}
|
|
98
|
+
function renderRuntimeStatus(runtime, out) {
|
|
99
|
+
const decisionStyle = runtime.decision === "ready" ? "ok" : runtime.decision === "blocked" ? "err" : "warn";
|
|
100
|
+
const deps = runtime.deps ? ` deps=${out(runtime.deps === "current" ? "ok" : "warn", runtime.deps)}` : "";
|
|
101
|
+
const devServer = runtime.devServer ? ` ${runtime.devServer.label}=${out(runtime.devServer.status === "up" ? "ok" : "warn", runtime.devServer.status)}` : "";
|
|
102
|
+
console.log(
|
|
103
|
+
`${out("label", "runtime:")} decision=${out(decisionStyle, runtime.decision)}${runtime.reasonCode ? ` ${out("dim", `(${runtime.reasonCode})`)}` : ""}${deps}${devServer}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
function nextForRuntime(launchHint, relaunchHint, runtime) {
|
|
107
|
+
if (runtime.decision === "ready") return "mm-harness logs";
|
|
108
|
+
if (runtime.decision === "relaunch") return launchHint;
|
|
109
|
+
return relaunchHint;
|
|
110
|
+
}
|
|
81
111
|
export {
|
|
82
112
|
handleStatus
|
|
83
113
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@farmslot/agent-runtime": "^0.1.
|
|
20
|
-
"@farmslot/protocol": "^0.7.
|
|
21
|
-
"@farmslot/recipe-harness": "^0.4.
|
|
19
|
+
"@farmslot/agent-runtime": "^0.1.1",
|
|
20
|
+
"@farmslot/protocol": "^0.7.6",
|
|
21
|
+
"@farmslot/recipe-harness": "^0.4.3",
|
|
22
22
|
"commander": "^12.0.0",
|
|
23
23
|
"viem": "^2.54.3"
|
|
24
24
|
},
|
|
@@ -69,4 +69,4 @@
|
|
|
69
69
|
"url": "https://github.com/MetaMask/experimental-metamask-harness/issues"
|
|
70
70
|
},
|
|
71
71
|
"homepage": "https://github.com/MetaMask/experimental-metamask-harness#readme"
|
|
72
|
-
}
|
|
72
|
+
}
|
package/scripts/completions.sh
CHANGED
|
File without changes
|
|
File without changes
|