@deeeed/metamask-harness 0.17.3 → 0.17.5
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 +6 -96
- package/README.md +136 -96
- package/adapters/extension/console-tail.mjs +24 -51
- package/adapters/extension/ensure-browser.sh +7 -1
- package/adapters/extension/inject.mjs +15 -5
- package/adapters/extension/launch-browser.cjs +4 -3
- package/adapters/extension/launch.sh +11 -8
- package/adapters/extension/lib/chrome-args.cjs +24 -0
- package/adapters/extension/live.sh +1 -2
- package/adapters/extension/reattach.sh +0 -1
- package/adapters/extension/seed-fixture.sh +12 -4
- package/adapters/extension/sidepanel-toggle.sh +1 -4
- package/adapters/extension/start-watch.sh +4 -17
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/verify.sh +1 -1
- package/adapters/manifest.json +45 -53
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -16
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +1 -3
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +3 -2
- package/adapters/mobile/cleanup.sh +176 -16
- package/adapters/mobile/inject.sh +671 -90
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/adapters/mobile/overlay/compat/README.md +28 -0
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +42 -0
- package/adapters/mobile/start-metro.sh +41 -0
- package/adapters/mobile/verify.sh +61 -4
- package/adapters/mobile/wait-for-bridge.sh +8 -22
- package/adapters/shared/harness-source-fingerprint.mjs +49 -0
- package/adapters/shared/install-repo-deps.sh +1 -5
- package/adapters/shared/open-debug.mjs +184 -103
- package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +5 -0
- package/adapters/shared/resolve-farmslot-ports.sh +7 -0
- package/adapters/shared/resolve-slot-ports-core.mjs +6 -0
- package/adapters/shared/resolve-slot-ports.sh +7 -2
- package/bin/mm-harness +0 -7
- package/dist/adapters/extension/harness-freshness.js +39 -0
- package/dist/adapters/extension/runtime-decision.js +4 -73
- package/dist/adapters/extension/surface.js +2 -3
- package/dist/adapters/mobile/perps-env.js +3 -43
- package/dist/adapters/mobile/prepare.js +7 -22
- package/dist/adapters/mobile/source-freshness.js +26 -41
- package/dist/adapters/mobile/surface.js +1 -1
- package/dist/adapters/resolve-slot-ports.js +2 -0
- package/dist/adapters/slot-ports.js +0 -16
- package/dist/adapters.js +5 -12
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +12 -6
- package/dist/commands/call.js +22 -56
- package/dist/commands/check.js +3 -9
- package/dist/commands/checklist.js +4 -1
- package/dist/commands/debug.js +31 -38
- package/dist/commands/device-target.js +12 -27
- package/dist/commands/doctor.js +23 -77
- package/dist/commands/fixtures.js +17 -27
- package/dist/commands/launch/extension.js +2 -0
- package/dist/commands/launch/index.js +59 -167
- package/dist/commands/logs.js +6 -8
- package/dist/commands/manifest.js +25 -151
- package/dist/commands/parse-args.js +0 -2
- package/dist/commands/provision.js +3 -10
- package/dist/commands/run-engine.js +69 -71
- package/dist/commands/run-report.js +3 -12
- package/dist/commands/run.js +35 -245
- package/dist/commands/shared.js +3 -55
- package/dist/commands/status-probe.js +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/stop.js +2 -7
- package/dist/commands/update.js +28 -16
- package/dist/doctor.js +4 -1
- package/dist/harness.js +134 -43
- package/dist/heal-bounds.js +1 -1
- package/dist/live-adapter-contract.js +20 -6
- package/dist/mm-harness-cli.js +48 -123
- package/dist/paths.js +5 -2
- package/dist/runner.js +2 -37
- package/dist/runtime-context.js +8 -26
- package/docs/ADAPTER-SURFACE.md +112 -19
- package/docs/CHEATSHEET.md +52 -39
- package/docs/CLI-SPEC.md +1098 -0
- package/docs/CODE-MAP.md +1 -1
- package/docs/DEBUG-HANDOVER.md +36 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/UX-PRINCIPLES.md +1 -3
- package/docs/architecture.md +375 -65
- package/docs/live-adapter-contract.md +2 -4
- package/docs/recipe-libraries.md +275 -45
- package/library/README.md +0 -4
- package/library/actions/core/perps/_controller.mjs +55 -10
- package/library/actions/mobile/platform/bridge.mjs +1 -5
- package/library/manifests/core.action-manifest.json +7 -7
- package/library/manifests/mobile.action-manifest.json +3 -28
- package/package.json +1 -4
- package/adapters/extension/launch-webpack.cjs +0 -83
- package/adapters/extension/stamp-runtime-title.cjs +0 -49
- package/adapters/extension/sync-webpack-dist.cjs +0 -105
- package/adapters/mobile/launch-console-forwarder.cjs +0 -39
- package/adapters/mobile/start-console-forwarder.sh +0 -70
- package/adapters/shared/update-check-worker.mjs +0 -35
- package/dist/adapters/extension/console-capture.js +0 -93
- package/dist/command-contract.js +0 -441
- package/dist/command-journal.js +0 -225
- package/dist/commands/last.js +0 -52
- package/dist/json-stream.js +0 -57
- package/dist/run-diagnostics.js +0 -261
- package/docs/CLI-ERGONOMICS-AUDIT.md +0 -32
- package/docs/CLI-ERGONOMICS-HUMAN-QA.md +0 -64
- package/docs/VIDEO-DEMO-VALIDATION.md +0 -74
- package/library/recipes/runner/smoke.core.recipe.json +0 -27
- package/library/recipes/wallet/smoke.extension.recipe.json +0 -42
- package/library/recipes/wallet/smoke.mobile.recipe.json +0 -42
- package/scripts/validate-human-outcomes.mjs +0 -169
|
@@ -24,6 +24,7 @@ import path from 'node:path';
|
|
|
24
24
|
import { spawnSync } from 'node:child_process';
|
|
25
25
|
import process from 'node:process';
|
|
26
26
|
import { recipeHarnessRoot } from '../shared/recipe-harness-root.mjs';
|
|
27
|
+
import { harnessSourceFingerprint } from '../shared/harness-source-fingerprint.mjs';
|
|
27
28
|
|
|
28
29
|
function usage() {
|
|
29
30
|
console.error('Usage: inject-extension-harness.mjs [--target <metamask-extension>] [--no-git-exclude]');
|
|
@@ -94,16 +95,16 @@ copyDir(path.join(runnerDir, 'library/recipes'), path.join(harnessDir, 'runner/r
|
|
|
94
95
|
copyFile(path.join(runnerDir, 'library/library.json'), path.join(harnessDir, 'runner/library.json'));
|
|
95
96
|
fs.mkdirSync(path.join(harnessDir, 'runner/flows'), { recursive: true });
|
|
96
97
|
copyDir(path.join(runnerDir, 'library/flows'), path.join(harnessDir, 'runner/flows'));
|
|
97
|
-
//
|
|
98
|
-
//
|
|
98
|
+
// Installed-layout back-compat (INTENTIONAL): targets keep a flat
|
|
99
|
+
// <harness>/scripts/ subtree assembled from the runner trees, so installed
|
|
100
|
+
// consumers are unaffected by runner-repo layout changes.
|
|
99
101
|
copyFile(path.join(runnerDir, 'adapters/extension/launch-browser.cjs'), path.join(harnessDir, 'scripts/launch-browser.cjs'));
|
|
102
|
+
// installed back-compat alias: callers of the pre-rename installed name
|
|
103
|
+
copyFile(path.join(runnerDir, 'adapters/extension/launch-browser.cjs'), path.join(harnessDir, 'scripts/launch-chrome-detached.cjs'));
|
|
100
104
|
copyFile(path.join(runnerDir, 'adapters/extension/console-tail.mjs'), path.join(harnessDir, 'scripts/console-tail.mjs'));
|
|
101
105
|
copyFile(path.join(runnerDir, 'adapters/extension/launch.sh'), path.join(harnessDir, 'scripts/launch.sh'));
|
|
102
106
|
copyFile(path.join(runnerDir, 'adapters/extension/live.sh'), path.join(harnessDir, 'scripts/live.sh'));
|
|
103
107
|
copyFile(path.join(runnerDir, 'adapters/extension/start-watch.sh'), path.join(harnessDir, 'scripts/start-watch.sh'));
|
|
104
|
-
copyFile(path.join(runnerDir, 'adapters/extension/launch-webpack.cjs'), path.join(harnessDir, 'scripts/launch-webpack.cjs'));
|
|
105
|
-
copyFile(path.join(runnerDir, 'adapters/extension/sync-webpack-dist.cjs'), path.join(harnessDir, 'scripts/sync-webpack-dist.cjs'));
|
|
106
|
-
copyFile(path.join(runnerDir, 'adapters/extension/stamp-runtime-title.cjs'), path.join(harnessDir, 'scripts/stamp-runtime-title.cjs'));
|
|
107
108
|
copyFile(path.join(runnerDir, 'adapters/extension/stop-viewers.sh'), path.join(harnessDir, 'scripts/stop-viewers.sh'));
|
|
108
109
|
copyFile(path.join(runnerDir, 'adapters/extension/snapshot-dist.sh'), path.join(harnessDir, 'scripts/snapshot-dist.sh'));
|
|
109
110
|
copyFile(path.join(runnerDir, 'adapters/extension/pin-remote-flags.cjs'), path.join(harnessDir, 'scripts/pin-remote-flags.cjs'));
|
|
@@ -120,6 +121,10 @@ copyFile(path.join(runnerDir, 'adapters/shared/activate-repo-node.sh'), path.joi
|
|
|
120
121
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports.sh'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports.sh'));
|
|
121
122
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports.mjs'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports.mjs'));
|
|
122
123
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports-core.mjs'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports-core.mjs'));
|
|
124
|
+
// Back-compat shims (one release) so an overlay consumer using the old leaf names still resolves.
|
|
125
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports.sh'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports.sh'));
|
|
126
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports.mjs'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports.mjs'));
|
|
127
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports-core.mjs'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports-core.mjs'));
|
|
123
128
|
copyFile(path.join(runnerDir, 'adapters/shared/path-defaults.json'), path.join(harnessDir, 'scripts/lib/path-defaults.json'));
|
|
124
129
|
copyFile(path.join(runnerDir, 'adapters/shared/json-field.sh'), path.join(harnessDir, 'scripts/lib/json-field.sh'));
|
|
125
130
|
copyFile(path.join(runnerDir, 'adapters/shared/tmux-session.sh'), path.join(harnessDir, 'scripts/lib/tmux-session.sh'));
|
|
@@ -127,6 +132,7 @@ copyFile(path.join(runnerDir, 'adapters/shared/tmux-viewer.sh'), path.join(harne
|
|
|
127
132
|
copyFile(path.join(runnerDir, 'adapters/shared/log-tui.mjs'), path.join(harnessDir, 'scripts/lib/log-tui.mjs'));
|
|
128
133
|
copyFile(path.join(runnerDir, 'adapters/extension/lib/extension-id.cjs'), path.join(harnessDir, 'scripts/lib/extension-id.cjs'));
|
|
129
134
|
copyFile(path.join(runnerDir, 'adapters/extension/lib/slot-title.cjs'), path.join(harnessDir, 'scripts/lib/slot-title.cjs'));
|
|
135
|
+
copyFile(path.join(runnerDir, 'adapters/extension/lib/chrome-args.cjs'), path.join(harnessDir, 'scripts/lib/chrome-args.cjs'));
|
|
130
136
|
makeExecutableTree(path.join(harnessDir, 'scripts'));
|
|
131
137
|
fs.writeFileSync(path.join(harnessDir, 'installed-scripts.sha256'), `${dirContentHash(path.join(harnessDir, 'scripts'))}\n`);
|
|
132
138
|
|
|
@@ -136,6 +142,10 @@ const manifest = {
|
|
|
136
142
|
adapter: 'extension',
|
|
137
143
|
installedAt: new Date().toISOString(),
|
|
138
144
|
runnerPackageVersion,
|
|
145
|
+
// Fingerprint of the runner source that produced this overlay. The launch-time
|
|
146
|
+
// freshness guard re-injects when the runner's fingerprint no longer matches,
|
|
147
|
+
// so a slot never silently runs a stale injected launcher.
|
|
148
|
+
sourceFingerprint: harnessSourceFingerprint(runnerDir),
|
|
139
149
|
source: {
|
|
140
150
|
runnerDir,
|
|
141
151
|
runnerRevision,
|
|
@@ -24,6 +24,7 @@ const os = require('node:os');
|
|
|
24
24
|
const path = require('node:path');
|
|
25
25
|
const { execFileSync, spawn, spawnSync } = require('node:child_process');
|
|
26
26
|
const { extensionIdFromExtensionDir } = require('./lib/extension-id.cjs');
|
|
27
|
+
const { remoteDebuggingArgs } = require('./lib/chrome-args.cjs');
|
|
27
28
|
|
|
28
29
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
29
30
|
console.log(
|
|
@@ -92,8 +93,9 @@ if (args['stop-only'] !== undefined) {
|
|
|
92
93
|
const initialUrl = args['start-url'] || extensionHomeUrl(args['extension-dir']) || 'chrome://extensions/';
|
|
93
94
|
const chromeArgs = [
|
|
94
95
|
`--user-data-dir=${args.profile}`,
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
// Debug-port trio (address + port + scoped allow-origins) from the shared module,
|
|
97
|
+
// so the DevTools origin allow-list can never drift away from the port.
|
|
98
|
+
...remoteDebuggingArgs(cdpPort),
|
|
97
99
|
'--no-first-run',
|
|
98
100
|
'--disable-first-run-ui',
|
|
99
101
|
'--disable-default-apps',
|
|
@@ -113,7 +115,6 @@ let browserPid;
|
|
|
113
115
|
try {
|
|
114
116
|
const application = process.platform === 'darwin' ? macApplicationForExecutable(args['chrome-bin']) : null;
|
|
115
117
|
if (application) {
|
|
116
|
-
// Launch Services does not attach Chrome's stdio, so chrome.log may stay empty; CDP ownership is the readiness signal.
|
|
117
118
|
execFileSync('open', ['-g', '-n', '-a', application, '--args', ...chromeArgs], {
|
|
118
119
|
env: sanitizedChildEnv(),
|
|
119
120
|
stdio: ['ignore', logFd, logFd],
|
|
@@ -114,21 +114,24 @@ else
|
|
|
114
114
|
status="fail"
|
|
115
115
|
fi
|
|
116
116
|
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
117
|
+
# Live extension-console viewer tab (default-on): tail the running extension's
|
|
118
|
+
# service worker + UI-page console in a tmux window, mirroring the Metro/webpack
|
|
119
|
+
# tabs. Skipped silently when there is no tmux session or CDP (headless/CI), so
|
|
120
|
+
# it never affects the launch result.
|
|
120
121
|
if [ "$status" = "pass" ] && [ -n "$CDP_PORT" ] && command -v tmux >/dev/null 2>&1; then
|
|
121
|
-
console_runtime="$TARGET/$( (cd "$TARGET" && recipe_runtime_dir) 2>/dev/null || echo temp/recipe/runtime)"
|
|
122
|
-
mkdir -p "$console_runtime"
|
|
123
|
-
console_log="$console_runtime/extension-console.log"
|
|
124
122
|
for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-viewer.sh"; do
|
|
125
123
|
# shellcheck disable=SC1091
|
|
126
124
|
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
127
125
|
done
|
|
128
126
|
unset _tv_src
|
|
129
|
-
|
|
127
|
+
console_tail="$SCRIPT_DIR/console-tail.mjs"
|
|
128
|
+
if command -v tmux_viewer_open >/dev/null 2>&1 && [ -f "$console_tail" ]; then
|
|
129
|
+
console_runtime="$TARGET/$( (cd "$TARGET" && recipe_runtime_dir) 2>/dev/null || echo temp/recipe/runtime)"
|
|
130
|
+
mkdir -p "$console_runtime"
|
|
131
|
+
console_log="$console_runtime/extension-console.log"
|
|
132
|
+
: > "$console_log"
|
|
130
133
|
tmux_viewer_open "console" "$CDP_PORT" "$console_log" "$console_runtime" "$console_runtime/console.tmux" \
|
|
131
|
-
"exec
|
|
134
|
+
"exec node $(printf '%q' "$console_tail") --cdp-port ${CDP_PORT} --log $(printf '%q' "$console_log")"
|
|
132
135
|
fi
|
|
133
136
|
fi
|
|
134
137
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// chrome-args.cjs — single source of truth for the remote-debugging launch flags
|
|
3
|
+
// shared by BOTH extension launchers: the fresh spawn (launch-browser.cjs) and the
|
|
4
|
+
// reopen path (ensure-browser.sh → reopen-browser.sh). Keeping the debug-port trio
|
|
5
|
+
// here means `--remote-allow-origins` can never drift away from the port it scopes:
|
|
6
|
+
// the two are declared once and always travel together. Without the allow-origins
|
|
7
|
+
// entry Chrome 403s every browser DevTools WebSocket on the debug port and
|
|
8
|
+
// `mm-harness debug` cannot attach a visible console.
|
|
9
|
+
|
|
10
|
+
// The exact origin allowed is the locally-served bundled DevTools frontend, which
|
|
11
|
+
// Chrome serves at http://127.0.0.1:<cdpPort>. Scoped to that origin — never `*`.
|
|
12
|
+
function remoteDebuggingArgs(cdpPort) {
|
|
13
|
+
const port = Number(cdpPort);
|
|
14
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
15
|
+
throw new Error(`remoteDebuggingArgs: invalid cdp port: ${cdpPort}`);
|
|
16
|
+
}
|
|
17
|
+
return [
|
|
18
|
+
'--remote-debugging-address=127.0.0.1',
|
|
19
|
+
`--remote-debugging-port=${port}`,
|
|
20
|
+
`--remote-allow-origins=http://127.0.0.1:${port}`,
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { remoteDebuggingArgs };
|
|
@@ -100,6 +100,7 @@ if [ -z "$VERIFY_SH" ]; then
|
|
|
100
100
|
echo "mm-harness: extension verify.sh not found next to $SCRIPT_DIR; reinstall the runner." >&2
|
|
101
101
|
exit 1
|
|
102
102
|
fi
|
|
103
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
103
104
|
RUNTIME_DIR="$(recipe_runtime_dir)"
|
|
104
105
|
RUNTIME_DIST_DIR="${RECIPE_RUNTIME_DIST_DIR:-runtime-dist}"
|
|
105
106
|
CHROME_PROFILE_NAME="${RECIPE_CHROME_PROFILE_NAME:-chrome-profile}"
|
|
@@ -162,7 +163,6 @@ NODE
|
|
|
162
163
|
quoted_seed_fixture="$(printf '%q' "$SCRIPT_DIR/seed-fixture.sh")"
|
|
163
164
|
quoted_start_watch="$(printf '%q' "$SCRIPT_DIR/start-watch.sh")"
|
|
164
165
|
quoted_snapshot_dist="$(printf '%q' "$SCRIPT_DIR/snapshot-dist.sh")"
|
|
165
|
-
quoted_stamp_title="$(printf '%q' "$SCRIPT_DIR/stamp-runtime-title.cjs")"
|
|
166
166
|
quoted_chrome_launcher="$(printf '%q' "$SCRIPT_DIR/launch-browser.cjs")"
|
|
167
167
|
quoted_fixture_state="$(printf '%q' "$FIXTURE_STATE_ABS")"
|
|
168
168
|
quoted_fixture_validation="$(printf '%q' "$FIXTURE_VALIDATION_ABS")"
|
|
@@ -257,7 +257,6 @@ NODE
|
|
|
257
257
|
fi
|
|
258
258
|
prepare_parts+=("node ${quoted_chrome_launcher} --stop-only 1 --reset-profile 1 --chrome-bin ${quoted_chrome} --profile ${quoted_profile} --cdp-port ${CDP_PORT} --extension-dir ${quoted_runtime_dist} --chrome-log ${quoted_chrome_log} --chrome-pid ${quoted_chrome_pid}")
|
|
259
259
|
prepare_parts+=("bash ${quoted_snapshot_dist} --dist ${quoted_dist} --runtime-dist ${quoted_runtime_dist}")
|
|
260
|
-
prepare_parts+=("node ${quoted_stamp_title} --target ${quoted_target} --runtime-dist ${quoted_runtime_dist} --runtime-dir ${quoted_runtime_dir}")
|
|
261
260
|
# Optional A/B feature-flag pinning: patch the ephemeral snapshot manifest so
|
|
262
261
|
# manifest._flags wins over the fetched ClientConfigApi value. No-op unless
|
|
263
262
|
# --remote-flag was passed; the snapshot is re-created each run, so no revert.
|
|
@@ -103,7 +103,6 @@ echo "[reattach] reusing loaded runtime-dist: $RUNTIME_DIST_ABS" >&2
|
|
|
103
103
|
if [ -d "$DIST_ABS" ] && [ -d "$RUNTIME_DIST_ABS" ]; then
|
|
104
104
|
echo "[reattach] refreshing loaded runtime-dist in place" >&2
|
|
105
105
|
rsync -a --exclude _metadata "$DIST_ABS/" "$RUNTIME_DIST_ABS/" >&2
|
|
106
|
-
node "$SCRIPT_DIR/stamp-runtime-title.cjs" --target "$TARGET" --runtime-dist "$RUNTIME_DIST_ABS" --runtime-dir "$RUNTIME_DIR" >&2
|
|
107
106
|
fi
|
|
108
107
|
|
|
109
108
|
# Resolve the extension id: explicit flag, then the recorded id file, else CDP.
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Purpose:
|
|
6
6
|
# Resolves which wallet fixture a slot run should use (env override then
|
|
7
|
-
#
|
|
7
|
+
# runtime-dir/legacy/port-scoped candidates) and drives the recipe-tree
|
|
8
8
|
# wallet-fixture-state.cjs phases around the browser launch.
|
|
9
9
|
#
|
|
10
10
|
# Subcommands (flags):
|
|
11
11
|
# resolve --target <repo> --cdp-port <port> [--source-out <file>]
|
|
12
12
|
# Prints the fixture path (empty if none). Chain:
|
|
13
|
-
# RECIPE_WALLET_FIXTURE env > <runtime-dir>/wallet-fixture.json
|
|
13
|
+
# RECIPE_WALLET_FIXTURE env > <runtime-dir>/wallet-fixture.json >
|
|
14
|
+
# temp/runtime/ > temp/.recipe-validation-<port>/ >
|
|
15
|
+
# temp/.agent-validation/.
|
|
14
16
|
# prefill --fixture <json> --target <repo> --state <file> --profile <dir>
|
|
15
17
|
# --extension-dir <dist> --extension-id-file <file>
|
|
16
18
|
# Runs fixture-state generate then prefill-profile (pre-launch).
|
|
@@ -129,8 +131,14 @@ if [ "$SUBCOMMAND" = resolve ]; then
|
|
|
129
131
|
fi
|
|
130
132
|
resolved="$RECIPE_WALLET_FIXTURE"
|
|
131
133
|
else
|
|
132
|
-
candidate
|
|
133
|
-
|
|
134
|
+
for candidate in \
|
|
135
|
+
${RUNTIME_DIR:+"$TARGET/$RUNTIME_DIR/wallet-fixture.json"} \
|
|
136
|
+
"$TARGET/temp/runtime/wallet-fixture.json" \
|
|
137
|
+
${CDP_PORT:+"$TARGET/temp/.recipe-validation-${CDP_PORT}/wallet-fixture.json"} \
|
|
138
|
+
"$TARGET/temp/.agent-validation/wallet-fixture.json"
|
|
139
|
+
do
|
|
140
|
+
[ -f "$candidate" ] && { resolved="$candidate"; break; }
|
|
141
|
+
done
|
|
134
142
|
fi
|
|
135
143
|
[ -n "$resolved" ] || resolve_status=missing
|
|
136
144
|
if [ -n "$SOURCE_OUT" ]; then
|
|
@@ -289,10 +289,7 @@ const { chromium } = require('playwright');
|
|
|
289
289
|
return { windowId: currentWindow.id };
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
// gesture. Its visual layout is irrelevant and can be occluded by the app's
|
|
294
|
-
// full-screen loading surface, so do not wait on Playwright actionability.
|
|
295
|
-
await page.locator('#__recipe_open_sidepanel__').click({ timeout: 5000, force: true });
|
|
292
|
+
await page.locator('#__recipe_open_sidepanel__').click({ timeout: 5000 });
|
|
296
293
|
const clickResult = await page
|
|
297
294
|
.locator('#__recipe_open_sidepanel__')
|
|
298
295
|
.evaluate((button) => button.dataset.result || '');
|
|
@@ -182,15 +182,9 @@ rm -rf node_modules/.cache/webpack
|
|
|
182
182
|
killed_harness_pid=""
|
|
183
183
|
if [ -f "$RUNTIME_DIR/recipe-harness-webpack.pid" ]; then
|
|
184
184
|
killed_harness_pid="$(cat "$RUNTIME_DIR/recipe-harness-webpack.pid" 2>/dev/null || true)"
|
|
185
|
-
[ -z "$killed_harness_pid" ] || kill -- "-$killed_harness_pid" >/dev/null 2>&1 || true
|
|
186
185
|
[ -z "$killed_harness_pid" ] || kill "$killed_harness_pid" >/dev/null 2>&1 || true
|
|
187
186
|
rm -f "$RUNTIME_DIR/recipe-harness-webpack.pid"
|
|
188
187
|
fi
|
|
189
|
-
if [ -f "$RUNTIME_DIR/recipe-harness-webpack-sync.pid" ]; then
|
|
190
|
-
sync_pid="$(cat "$RUNTIME_DIR/recipe-harness-webpack-sync.pid" 2>/dev/null || true)"
|
|
191
|
-
[ -z "$sync_pid" ] || kill "$sync_pid" >/dev/null 2>&1 || true
|
|
192
|
-
rm -f "$RUNTIME_DIR/recipe-harness-webpack-sync.pid"
|
|
193
|
-
fi
|
|
194
188
|
|
|
195
189
|
# Do not start a second writer for dist/chrome. Slot-owned watches use
|
|
196
190
|
# webpack.pid; harness-owned watches use recipe-harness-webpack.pid.
|
|
@@ -225,8 +219,6 @@ fi
|
|
|
225
219
|
# unrelated repo and leave this target validating stale dist/chrome output.
|
|
226
220
|
watch_pid_file="$RUNTIME_DIR/recipe-harness-webpack.pid"
|
|
227
221
|
watch_log="$RUNTIME_DIR/recipe-harness-webpack.log"
|
|
228
|
-
sync_pid_file="$RUNTIME_DIR/recipe-harness-webpack-sync.pid"
|
|
229
|
-
sync_log="$RUNTIME_DIR/recipe-harness-webpack-sync.log"
|
|
230
222
|
rm -f "$watch_pid_file"
|
|
231
223
|
: > "$watch_log"
|
|
232
224
|
LIB_DIR=""
|
|
@@ -237,16 +229,11 @@ unset _lib
|
|
|
237
229
|
echo "[recipe-harness] Starting yarn start (watcher-port=${WATCHER_PORT:-default})"
|
|
238
230
|
echo "[recipe-harness] compact build view - full log: $watch_log (mm-harness logs --full)"
|
|
239
231
|
started_pid=""
|
|
232
|
+
# Launch the watcher detached writing to the log; the tmux window is a read-only tail viewer.
|
|
240
233
|
path_prefix="${REPO_NODE_PATH_PREFIX:-}"
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
[ -f "$sync_script" ] || { echo "sync-webpack-dist.cjs missing from harness; run: mm-harness install" >&2; exit 1; }
|
|
245
|
-
launch_args=(--target "$PWD" --log "$watch_log" --pid-file "$watch_pid_file" --sync-script "$sync_script" --sync-pid-file "$sync_pid_file" --sync-log "$sync_log" --dist "$PWD/dist/chrome" --runtime-dist "$PWD/$RUNTIME_DIR/${RECIPE_RUNTIME_DIST_DIR:-runtime-dist}")
|
|
246
|
-
[ -z "$WATCHER_PORT" ] || launch_args+=(--port "$WATCHER_PORT")
|
|
247
|
-
[ -z "$path_prefix" ] || launch_args+=(--path-prefix "$path_prefix")
|
|
248
|
-
env -u BUNDLED_DEBUGPY_PATH node "$launcher" "${launch_args[@]}" >/dev/null
|
|
249
|
-
started_pid="$(cat "$watch_pid_file")"
|
|
234
|
+
nohup env -u BUNDLED_DEBUGPY_PATH ${path_prefix:+PATH="${path_prefix}${PATH}"} ${WATCHER_PORT:+PORT="$WATCHER_PORT"} yarn start > "$watch_log" 2>&1 &
|
|
235
|
+
started_pid=$!
|
|
236
|
+
printf '%s\n' "$started_pid" > "$watch_pid_file"
|
|
250
237
|
# The orchestrator names the session; the harness only populates windows. Resolve
|
|
251
238
|
# via the run-owned ladder (RECIPE_TMUX_SESSION → agentic-runtime.json session) —
|
|
252
239
|
# no current-session fallback or slot-number derivation.
|
package/adapters/manifest.json
CHANGED
|
@@ -18,22 +18,6 @@
|
|
|
18
18
|
"inputs": "--target --port --clear; env WATCHER_PORT, METRO_PORT, MOBILE_METRO_REQUIRED_ENV, METRO_MAX_WORKERS",
|
|
19
19
|
"outputs": "Metro PID file + log; exit 0 ready / 1 timeout / 2 bad args"
|
|
20
20
|
},
|
|
21
|
-
{
|
|
22
|
-
"id": "mobile/start-console-forwarder",
|
|
23
|
-
"entry": "adapters/mobile/start-console-forwarder.sh",
|
|
24
|
-
"kind": "bash",
|
|
25
|
-
"purpose": "Ensure one runner-owned Mobile application console collector is alive after bridge readiness.",
|
|
26
|
-
"inputs": "--target --port --quiet; env WATCHER_PORT, METRO_PORT, METAMASK_RECIPE_CONSOLE_FORWARD",
|
|
27
|
-
"outputs": "app-console.log + collector PID file; exit 0 ready / 1 startup failed / 2 bad args"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"id": "mobile/launch-console-forwarder",
|
|
31
|
-
"entry": "adapters/mobile/launch-console-forwarder.cjs",
|
|
32
|
-
"kind": "node",
|
|
33
|
-
"purpose": "Detach the Mobile application console collector from command-runner process-group cleanup.",
|
|
34
|
-
"inputs": "--port --out --err",
|
|
35
|
-
"outputs": "collector PID on stdout; exit 0 launched / 1 spawn failed / 2 bad args"
|
|
36
|
-
},
|
|
37
21
|
{
|
|
38
22
|
"id": "mobile/launch-metro",
|
|
39
23
|
"entry": "adapters/mobile/launch-metro.cjs",
|
|
@@ -78,17 +62,17 @@
|
|
|
78
62
|
"id": "mobile/inject",
|
|
79
63
|
"entry": "adapters/mobile/inject.sh",
|
|
80
64
|
"kind": "bash",
|
|
81
|
-
"purpose": "Install
|
|
82
|
-
"inputs": "--target --no-git-exclude; env RECIPE_HARNESS_ROOT, METAMASK_RUNNER_PROTOCOL_ROOT",
|
|
83
|
-
"outputs": "<harness>/manifest.json + runner/ + scripts
|
|
65
|
+
"purpose": "Install the mobile harness overlay (delegate + snapshots; full mode adds the app bridge with backups).",
|
|
66
|
+
"inputs": "--target --allow-dirty-harness-paths --force-overlay --no-git-exclude; env RECIPE_HARNESS_ROOT, METAMASK_RUNNER_PROTOCOL_ROOT>FARMSLOT_ROOT",
|
|
67
|
+
"outputs": "<harness>/manifest.json + runner/ + scripts/ + backup store; exit 0/1/2"
|
|
84
68
|
},
|
|
85
69
|
{
|
|
86
70
|
"id": "mobile/cleanup",
|
|
87
71
|
"entry": "adapters/mobile/cleanup.sh",
|
|
88
72
|
"kind": "bash",
|
|
89
|
-
"purpose": "Remove
|
|
90
|
-
"inputs": "--target; env RECIPE_HARNESS_ROOT",
|
|
91
|
-
"outputs": "removes <harness
|
|
73
|
+
"purpose": "Remove the mobile overlay; restore backed-up product files; remove recorded git excludes.",
|
|
74
|
+
"inputs": "--target --allow-managed-changes; env RECIPE_HARNESS_ROOT",
|
|
75
|
+
"outputs": "removes <harness>/ + backups; exit 0/1/2"
|
|
92
76
|
},
|
|
93
77
|
{
|
|
94
78
|
"id": "mobile/verify",
|
|
@@ -130,30 +114,6 @@
|
|
|
130
114
|
"inputs": "--target --runtime-dir --runner-bin --summary",
|
|
131
115
|
"outputs": "<runtime-dir>/recipe-harness-webpack.{pid,log}; optional summary; exit 0/1/2"
|
|
132
116
|
},
|
|
133
|
-
{
|
|
134
|
-
"id": "extension/launch-webpack",
|
|
135
|
-
"entry": "adapters/extension/launch-webpack.cjs",
|
|
136
|
-
"kind": "node",
|
|
137
|
-
"purpose": "Detach the normal yarn start watcher from the launching CLI process while preserving its target, environment, and log.",
|
|
138
|
-
"inputs": "--target --log --pid-file --sync-script --sync-pid-file --sync-log --dist --runtime-dist; optional --port --path-prefix",
|
|
139
|
-
"outputs": "watcher and runtime-sync pids; webpack and sync logs; exit 0/1"
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"id": "extension/sync-webpack-dist",
|
|
143
|
-
"entry": "adapters/extension/sync-webpack-dist.cjs",
|
|
144
|
-
"kind": "node",
|
|
145
|
-
"purpose": "After each successful webpack compile, update the loaded isolated runtime in place so an ordinary browser refresh sees the edit.",
|
|
146
|
-
"inputs": "--watcher-pid --watch-log --target --dist --runtime-dist --pid-file",
|
|
147
|
-
"outputs": "runtime-dist updated in place after successful compile markers; exits with its watcher"
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
"id": "extension/stamp-runtime-title",
|
|
151
|
-
"entry": "adapters/extension/stamp-runtime-title.cjs",
|
|
152
|
-
"kind": "node",
|
|
153
|
-
"purpose": "Stamp the slot id into isolated Extension page titles so window identity survives browser refreshes.",
|
|
154
|
-
"inputs": "--target --runtime-dist; optional --runtime-dir",
|
|
155
|
-
"outputs": "home.html and sidepanel.html titles updated in the isolated runtime only; exit 0/1"
|
|
156
|
-
},
|
|
157
117
|
{
|
|
158
118
|
"id": "extension/stop-viewers",
|
|
159
119
|
"entry": "adapters/extension/stop-viewers.sh",
|
|
@@ -418,6 +378,22 @@
|
|
|
418
378
|
"inputs": "ESM import from resolve-slot-ports.mjs or resolve-slot-ports.ts",
|
|
419
379
|
"outputs": "resolveSlotPortsByRepo and related functions"
|
|
420
380
|
},
|
|
381
|
+
{
|
|
382
|
+
"id": "lib/resolve-farmslot-ports",
|
|
383
|
+
"entry": "adapters/shared/resolve-farmslot-ports.mjs",
|
|
384
|
+
"kind": "node",
|
|
385
|
+
"purpose": "Back-compat shim (one release): delegates to resolve-slot-ports.mjs.",
|
|
386
|
+
"inputs": "resolve-farmslot-ports.mjs <fn> <repo>",
|
|
387
|
+
"outputs": "same as resolve-slot-ports.mjs"
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"id": "lib/resolve-farmslot-ports-core",
|
|
391
|
+
"entry": "adapters/shared/resolve-farmslot-ports-core.mjs",
|
|
392
|
+
"kind": "lib",
|
|
393
|
+
"purpose": "Back-compat shim (one release): re-exports resolve-slot-ports-core.mjs.",
|
|
394
|
+
"inputs": "ESM import of the old path",
|
|
395
|
+
"outputs": "re-exports resolveSlotPortsByRepo (and the resolveFarmslotPortsByRepo alias)"
|
|
396
|
+
},
|
|
421
397
|
{
|
|
422
398
|
"id": "lib/recipe-harness-root",
|
|
423
399
|
"entry": "adapters/shared/recipe-harness-root.mjs",
|
|
@@ -430,10 +406,18 @@
|
|
|
430
406
|
"id": "lib/resolve-slot-ports-sh",
|
|
431
407
|
"entry": "adapters/shared/resolve-slot-ports.sh",
|
|
432
408
|
"kind": "lib",
|
|
433
|
-
"purpose": "
|
|
409
|
+
"purpose": "Bash-compat sourced wrapper over resolve-slot-ports.mjs for stop-metro and legacy callers.",
|
|
434
410
|
"inputs": "source + resolve_* / apply_resolved_* function calls",
|
|
435
411
|
"outputs": "KEY=VALUE lines on stdout; env vars via apply_resolved_*"
|
|
436
412
|
},
|
|
413
|
+
{
|
|
414
|
+
"id": "lib/resolve-farmslot-ports-sh",
|
|
415
|
+
"entry": "adapters/shared/resolve-farmslot-ports.sh",
|
|
416
|
+
"kind": "lib",
|
|
417
|
+
"purpose": "Back-compat shim (one release): sources resolve-slot-ports.sh (keeps the resolve_farmslot_ports_by_repo alias).",
|
|
418
|
+
"inputs": "source",
|
|
419
|
+
"outputs": "same sourced functions as resolve-slot-ports.sh"
|
|
420
|
+
},
|
|
437
421
|
{
|
|
438
422
|
"id": "lib/reap-checkout-metros",
|
|
439
423
|
"entry": "adapters/shared/reap-checkout-metros.sh",
|
|
@@ -488,12 +472,20 @@
|
|
|
488
472
|
"outputs": "debug URL/report on stdout; exit 0/1"
|
|
489
473
|
},
|
|
490
474
|
{
|
|
491
|
-
"id": "lib/
|
|
492
|
-
"entry": "adapters/shared/
|
|
493
|
-
"kind": "
|
|
494
|
-
"purpose": "
|
|
495
|
-
"inputs": "
|
|
496
|
-
"outputs": "
|
|
475
|
+
"id": "lib/harness-source-fingerprint",
|
|
476
|
+
"entry": "adapters/shared/harness-source-fingerprint.mjs",
|
|
477
|
+
"kind": "lib",
|
|
478
|
+
"purpose": "Compute the deterministic source fingerprint stamped into injected extension harness manifests.",
|
|
479
|
+
"inputs": "imported by injection and launch freshness checks",
|
|
480
|
+
"outputs": "stable SHA-256 fingerprint"
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"id": "compat-overlays/mobile",
|
|
484
|
+
"entry": "adapters/mobile/overlay/compat",
|
|
485
|
+
"kind": "compat-overlay",
|
|
486
|
+
"purpose": "Historical-commit replay support: reversible source overlays (e.g. rn81-message-event-source.patch) applied manually/via the recipe-harness skill so pre-RN-0.81 mobile checkouts boot and bridge under the current toolchain.",
|
|
487
|
+
"inputs": "manual `git apply` in the historical product checkout (no in-repo apply logic)",
|
|
488
|
+
"outputs": "patched historical checkout (reversible via git apply -R); overlay path recorded in eval evidence"
|
|
497
489
|
}
|
|
498
490
|
]
|
|
499
491
|
}
|
|
@@ -254,21 +254,6 @@ const COMMANDS = {
|
|
|
254
254
|
return { ...result, testId, deviceName };
|
|
255
255
|
},
|
|
256
256
|
|
|
257
|
-
async 'press-text'(client, args, { deviceName } = {}) {
|
|
258
|
-
const text = args[0];
|
|
259
|
-
if (!text) {
|
|
260
|
-
throw new Error('Usage: press-text <text>');
|
|
261
|
-
}
|
|
262
|
-
const expr = `(function() {
|
|
263
|
-
if (typeof globalThis.__AGENTIC__?.pressText !== 'function') {
|
|
264
|
-
return { ok: false, error: 'The app bridge does not expose pressText' };
|
|
265
|
-
}
|
|
266
|
-
return globalThis.__AGENTIC__.pressText(${JSON.stringify(text)});
|
|
267
|
-
})()`;
|
|
268
|
-
const result = await cdpEval(client, expr);
|
|
269
|
-
return { ...result, text, deviceName };
|
|
270
|
-
},
|
|
271
|
-
|
|
272
257
|
async 'scroll-view'(client, args, { deviceName } = {}) {
|
|
273
258
|
let testId;
|
|
274
259
|
let offset = 300;
|
|
@@ -693,7 +678,6 @@ Commands:
|
|
|
693
678
|
get-selected-account Get the currently selected account
|
|
694
679
|
switch-account <address> Switch to account by address
|
|
695
680
|
press-test-id <testId> Press a component by its testID prop
|
|
696
|
-
press-text <text> Press a component containing visible text
|
|
697
681
|
scroll-view [--test-id <id>] [--offset <n>] [--animated]
|
|
698
682
|
Scroll a ScrollView/FlatList
|
|
699
683
|
set-input <testId> <value> Set text input value by testID (calls onChangeText)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// console-forwarder — stream device console lines into metro.log.
|
|
3
3
|
//
|
|
4
|
-
// React Native (Bridgeless) gates its
|
|
4
|
+
// React Native (Bridgeless) gates its legacy console→Metro forwarding on
|
|
5
5
|
// `console._isPolyfilled` (setUpDeveloperTools.js, T214991636); with the Hermes
|
|
6
6
|
// native console that gate is false, so app logs (incl. DevLogger) never reach
|
|
7
7
|
// Metro's log. The supported contract for log consumption in modern RN is CDP —
|
|
@@ -50,9 +50,7 @@ function matchesBridgeTarget(target, env) {
|
|
|
50
50
|
// (an in-app agentic bridge is live), not merely a registered debug target.
|
|
51
51
|
function hasMatchingRoute(value, env) {
|
|
52
52
|
const targets = Array.isArray(value) ? value : [value];
|
|
53
|
-
return targets.some(
|
|
54
|
-
(t) => matchesBridgeTarget(t, env) && t && t.agenticPresent === true && Boolean(t.route),
|
|
55
|
-
);
|
|
53
|
+
return targets.some((t) => matchesBridgeTarget(t, env) && t && t.route);
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
// Human summary of the targets that answered (platform / deviceName), for teaching on
|
|
@@ -136,7 +136,7 @@ async function discoverTarget(port) {
|
|
|
136
136
|
// ANDROID_TARGET_DEVICE_NAME — Metro-compatible model prefix resolved by bridge.mjs
|
|
137
137
|
// from the adb serial via `adb -s <serial> shell getprop ro.product.model`.
|
|
138
138
|
// Example: "Pixel 6" matches Metro deviceName "Pixel 6 - 16 - API 36".
|
|
139
|
-
// ANDROID_DEVICE — exact user-specified
|
|
139
|
+
// ANDROID_DEVICE — exact Metro deviceName (backward compat / user-specified).
|
|
140
140
|
const androidTargetName = loadAndroidTargetDeviceName();
|
|
141
141
|
const androidDevice = loadAndroidDevice();
|
|
142
142
|
const adbSerial = process.env.ADB_SERIAL || process.env.ANDROID_SERIAL || '';
|
|
@@ -205,7 +205,8 @@ async function discoverTarget(port) {
|
|
|
205
205
|
}
|
|
206
206
|
candidates = androidFiltered;
|
|
207
207
|
} else if (androidDevice) {
|
|
208
|
-
// Exact Metro deviceName match
|
|
208
|
+
// Exact Metro deviceName match (user set ANDROID_DEVICE to the Metro name, or
|
|
209
|
+
// legacy path where ANDROID_DEVICE was not resolved from serial).
|
|
209
210
|
androidFiltered = candidates.filter((t) => t.deviceName === androidDevice);
|
|
210
211
|
if (androidFiltered.length === 0) {
|
|
211
212
|
// Pinned by exact Metro name but no candidate matched — fail fast.
|