@deeeed/metamask-harness 0.17.0 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +90 -140
- package/adapters/core/cleanup.sh +0 -0
- package/adapters/core/inject.sh +0 -0
- package/adapters/extension/cleanup.mjs +0 -0
- package/adapters/extension/console-tail.mjs +51 -24
- package/adapters/extension/ensure-browser.sh +0 -0
- package/adapters/extension/inject.mjs +5 -9
- package/adapters/extension/launch-browser.cjs +0 -0
- package/adapters/extension/launch-webpack.cjs +83 -0
- package/adapters/extension/launch.sh +8 -11
- package/adapters/extension/live.sh +2 -0
- package/adapters/extension/readiness.mjs +0 -0
- package/adapters/extension/reattach.sh +1 -0
- package/adapters/extension/refresh-build.sh +0 -0
- package/adapters/extension/seed-fixture.sh +4 -12
- package/adapters/extension/sidepanel-toggle.sh +4 -1
- package/adapters/extension/snapshot-dist.sh +0 -0
- package/adapters/extension/stamp-runtime-title.cjs +49 -0
- package/adapters/extension/start-watch.sh +16 -4
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/sync-webpack-dist.cjs +105 -0
- package/adapters/extension/verify.sh +1 -1
- package/adapters/extension/wallet-fixture-state.cjs +0 -0
- package/adapters/manifest.json +46 -38
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
- package/adapters/mobile/bridge-runtime/setup-wallet.sh +0 -0
- package/adapters/mobile/cleanup.sh +16 -176
- package/adapters/mobile/inject.sh +88 -671
- package/adapters/mobile/launch-console-forwarder.cjs +39 -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-console-forwarder.sh +70 -0
- package/adapters/mobile/start-metro.sh +0 -41
- package/adapters/mobile/stop-metro.sh +0 -0
- package/adapters/mobile/verify.sh +4 -61
- 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/install-repo-deps.sh +0 -0
- package/adapters/shared/json-field.sh +0 -0
- package/adapters/shared/open-debug.mjs +90 -36
- package/adapters/shared/open-log-window.sh +0 -0
- package/adapters/shared/reap-checkout-metros.sh +0 -0
- package/adapters/shared/resolve-slot-ports-core.mjs +0 -6
- package/adapters/shared/resolve-slot-ports.mjs +0 -0
- package/adapters/shared/resolve-slot-ports.sh +0 -5
- package/adapters/shared/sync-wallet-fixture.sh +0 -0
- package/adapters/shared/tmux-session.sh +0 -0
- package/adapters/shared/tmux-viewer.sh +0 -0
- package/adapters/shared/update-check-worker.mjs +37 -0
- package/dist/adapters/extension/console-capture.js +84 -0
- package/dist/adapters/extension/runtime-decision.js +14 -2
- package/dist/adapters/extension/surface.js +3 -2
- package/dist/adapters/mobile/prepare.js +22 -7
- package/dist/adapters/mobile/surface.js +1 -1
- package/dist/adapters/resolve-slot-ports.js +0 -2
- package/dist/adapters/slot-ports.js +8 -0
- package/dist/cli.js +3 -11
- package/dist/commands/call.js +9 -4
- package/dist/commands/debug.js +38 -31
- package/dist/commands/fixtures.js +18 -10
- package/dist/commands/launch/index.js +10 -0
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +5 -17
- package/dist/commands/run-engine.js +1 -32
- package/dist/commands/run-report.js +1 -1
- package/dist/commands/run.js +50 -3
- package/dist/commands/status-probe.js +1 -1
- package/dist/commands/update.js +15 -28
- package/dist/doctor.js +1 -4
- package/dist/harness.js +5 -123
- package/dist/live-adapter-contract.js +6 -20
- package/dist/mm-harness-cli.js +6 -43
- package/dist/paths.js +2 -5
- package/dist/run-diagnostics.js +35 -40
- package/dist/runtime-context.js +26 -8
- package/docs/CHEATSHEET.md +39 -52
- package/docs/CLI-ERGONOMICS-HUMAN-QA.md +48 -104
- package/docs/CODE-MAP.md +1 -1
- package/docs/VIDEO-DEMO-VALIDATION.md +62 -0
- package/docs/architecture.md +65 -375
- package/docs/live-adapter-contract.md +3 -1
- package/library/actions/core/perps/_controller.mjs +10 -55
- package/library/manifests/mobile.action-manifest.json +1 -1
- package/package.json +2 -2
- package/scripts/completions.sh +0 -0
- package/scripts/install-completions.sh +0 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
- package/adapters/mobile/overlay/compat/README.md +0 -28
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
- package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
- package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
- package/adapters/shared/resolve-farmslot-ports.sh +0 -7
- package/docs/CLI-SPEC.md +0 -1142
- package/docs/MENTAL-MODEL.md +0 -295
|
@@ -114,24 +114,21 @@ else
|
|
|
114
114
|
status="fail"
|
|
115
115
|
fi
|
|
116
116
|
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# it never affects the launch result.
|
|
117
|
+
# The public CLI owns console collection after every successful launch path.
|
|
118
|
+
# This leaf only adds an optional viewer when an explicitly run-owned tmux
|
|
119
|
+
# session exists; the viewer tails the file and never owns the collector.
|
|
121
120
|
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"
|
|
122
124
|
for _tv_src in "$SCRIPT_DIR/lib/tmux-viewer.sh" "$SCRIPT_DIR/../shared/tmux-viewer.sh"; do
|
|
123
125
|
# shellcheck disable=SC1091
|
|
124
126
|
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
125
127
|
done
|
|
126
128
|
unset _tv_src
|
|
127
|
-
|
|
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"
|
|
129
|
+
if command -v tmux_viewer_open >/dev/null 2>&1; then
|
|
133
130
|
tmux_viewer_open "console" "$CDP_PORT" "$console_log" "$console_runtime" "$console_runtime/console.tmux" \
|
|
134
|
-
"exec
|
|
131
|
+
"exec tail -n +1 -F $(printf '%q' "$console_log")"
|
|
135
132
|
fi
|
|
136
133
|
fi
|
|
137
134
|
|
|
@@ -162,6 +162,7 @@ NODE
|
|
|
162
162
|
quoted_seed_fixture="$(printf '%q' "$SCRIPT_DIR/seed-fixture.sh")"
|
|
163
163
|
quoted_start_watch="$(printf '%q' "$SCRIPT_DIR/start-watch.sh")"
|
|
164
164
|
quoted_snapshot_dist="$(printf '%q' "$SCRIPT_DIR/snapshot-dist.sh")"
|
|
165
|
+
quoted_stamp_title="$(printf '%q' "$SCRIPT_DIR/stamp-runtime-title.cjs")"
|
|
165
166
|
quoted_chrome_launcher="$(printf '%q' "$SCRIPT_DIR/launch-browser.cjs")"
|
|
166
167
|
quoted_fixture_state="$(printf '%q' "$FIXTURE_STATE_ABS")"
|
|
167
168
|
quoted_fixture_validation="$(printf '%q' "$FIXTURE_VALIDATION_ABS")"
|
|
@@ -256,6 +257,7 @@ NODE
|
|
|
256
257
|
fi
|
|
257
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}")
|
|
258
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}")
|
|
259
261
|
# Optional A/B feature-flag pinning: patch the ephemeral snapshot manifest so
|
|
260
262
|
# manifest._flags wins over the fetched ClientConfigApi value. No-op unless
|
|
261
263
|
# --remote-flag was passed; the snapshot is re-created each run, so no revert.
|
|
File without changes
|
|
@@ -103,6 +103,7 @@ 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
|
|
106
107
|
fi
|
|
107
108
|
|
|
108
109
|
# Resolve the extension id: explicit flag, then the recorded id file, else CDP.
|
|
File without changes
|
|
@@ -4,15 +4,13 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Purpose:
|
|
6
6
|
# Resolves which wallet fixture a slot run should use (env override then
|
|
7
|
-
# runtime
|
|
7
|
+
# the checkout's canonical runtime fixture) 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
|
|
14
|
-
# temp/runtime/ > temp/.recipe-validation-<port>/ >
|
|
15
|
-
# temp/.agent-validation/.
|
|
13
|
+
# RECIPE_WALLET_FIXTURE env > <runtime-dir>/wallet-fixture.json.
|
|
16
14
|
# prefill --fixture <json> --target <repo> --state <file> --profile <dir>
|
|
17
15
|
# --extension-dir <dist> --extension-id-file <file>
|
|
18
16
|
# Runs fixture-state generate then prefill-profile (pre-launch).
|
|
@@ -131,14 +129,8 @@ if [ "$SUBCOMMAND" = resolve ]; then
|
|
|
131
129
|
fi
|
|
132
130
|
resolved="$RECIPE_WALLET_FIXTURE"
|
|
133
131
|
else
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
132
|
+
candidate="${RUNTIME_DIR:+$TARGET/$RUNTIME_DIR/wallet-fixture.json}"
|
|
133
|
+
[ -n "$candidate" ] && [ -f "$candidate" ] && resolved="$candidate"
|
|
142
134
|
fi
|
|
143
135
|
[ -n "$resolved" ] || resolve_status=missing
|
|
144
136
|
if [ -n "$SOURCE_OUT" ]; then
|
|
@@ -289,7 +289,10 @@ const { chromium } = require('playwright');
|
|
|
289
289
|
return { windowId: currentWindow.id };
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
-
|
|
292
|
+
// This harness-owned button exists only to provide Chrome's trusted user
|
|
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 });
|
|
293
296
|
const clickResult = await page
|
|
294
297
|
.locator('#__recipe_open_sidepanel__')
|
|
295
298
|
.evaluate((button) => button.dataset.result || '');
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const { readSlotId } = require('./lib/slot-title.cjs');
|
|
8
|
+
|
|
9
|
+
function stampRuntimeTitles({ target, runtimeDist, runtimeDir = process.env.RECIPE_RUNTIME_DIR || 'temp/recipe/runtime' }) {
|
|
10
|
+
const slotId = readSlotId(target, runtimeDir);
|
|
11
|
+
if (!slotId) return { slotId: '', stamped: [] };
|
|
12
|
+
const stamped = [];
|
|
13
|
+
for (const name of ['home.html', 'sidepanel.html']) {
|
|
14
|
+
const file = path.join(runtimeDist, name);
|
|
15
|
+
if (!fs.existsSync(file)) continue;
|
|
16
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
17
|
+
const next = source.replace(/<title>([^<]*)<\/title>/iu, (_match, title) => {
|
|
18
|
+
const base = String(title || 'MetaMask').replace(/^.+?\s+[—–-]\s+/u, '') || 'MetaMask';
|
|
19
|
+
return `<title>${slotId} — ${base}</title>`;
|
|
20
|
+
});
|
|
21
|
+
if (next === source) continue;
|
|
22
|
+
fs.writeFileSync(file, next);
|
|
23
|
+
stamped.push(name);
|
|
24
|
+
}
|
|
25
|
+
return { slotId, stamped };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (require.main === module) {
|
|
29
|
+
const args = {};
|
|
30
|
+
for (let index = 2; index < process.argv.length; index += 1) {
|
|
31
|
+
const key = process.argv[index];
|
|
32
|
+
if (key === '--help' || key === '-h') {
|
|
33
|
+
process.stdout.write('Usage: stamp-runtime-title.cjs --target <path> --runtime-dist <path> [--runtime-dir <path>]\n');
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
if (!key.startsWith('--') || index + 1 >= process.argv.length) throw new Error(`invalid argument: ${key}`);
|
|
37
|
+
args[key.slice(2)] = process.argv[++index];
|
|
38
|
+
}
|
|
39
|
+
if (!args.target) throw new Error('--target is required');
|
|
40
|
+
if (!args['runtime-dist']) throw new Error('--runtime-dist is required');
|
|
41
|
+
const result = stampRuntimeTitles({
|
|
42
|
+
target: path.resolve(args.target),
|
|
43
|
+
runtimeDist: path.resolve(args['runtime-dist']),
|
|
44
|
+
runtimeDir: args['runtime-dir'],
|
|
45
|
+
});
|
|
46
|
+
if (result.stamped.length > 0) process.stdout.write(`slot title ${result.slotId}: ${result.stamped.join(', ')}\n`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { stampRuntimeTitles };
|
|
@@ -185,6 +185,11 @@ if [ -f "$RUNTIME_DIR/recipe-harness-webpack.pid" ]; then
|
|
|
185
185
|
[ -z "$killed_harness_pid" ] || kill "$killed_harness_pid" >/dev/null 2>&1 || true
|
|
186
186
|
rm -f "$RUNTIME_DIR/recipe-harness-webpack.pid"
|
|
187
187
|
fi
|
|
188
|
+
if [ -f "$RUNTIME_DIR/recipe-harness-webpack-sync.pid" ]; then
|
|
189
|
+
sync_pid="$(cat "$RUNTIME_DIR/recipe-harness-webpack-sync.pid" 2>/dev/null || true)"
|
|
190
|
+
[ -z "$sync_pid" ] || kill "$sync_pid" >/dev/null 2>&1 || true
|
|
191
|
+
rm -f "$RUNTIME_DIR/recipe-harness-webpack-sync.pid"
|
|
192
|
+
fi
|
|
188
193
|
|
|
189
194
|
# Do not start a second writer for dist/chrome. Slot-owned watches use
|
|
190
195
|
# webpack.pid; harness-owned watches use recipe-harness-webpack.pid.
|
|
@@ -219,6 +224,8 @@ fi
|
|
|
219
224
|
# unrelated repo and leave this target validating stale dist/chrome output.
|
|
220
225
|
watch_pid_file="$RUNTIME_DIR/recipe-harness-webpack.pid"
|
|
221
226
|
watch_log="$RUNTIME_DIR/recipe-harness-webpack.log"
|
|
227
|
+
sync_pid_file="$RUNTIME_DIR/recipe-harness-webpack-sync.pid"
|
|
228
|
+
sync_log="$RUNTIME_DIR/recipe-harness-webpack-sync.log"
|
|
222
229
|
rm -f "$watch_pid_file"
|
|
223
230
|
: > "$watch_log"
|
|
224
231
|
LIB_DIR=""
|
|
@@ -229,11 +236,16 @@ unset _lib
|
|
|
229
236
|
echo "[recipe-harness] Starting yarn start (watcher-port=${WATCHER_PORT:-default})"
|
|
230
237
|
echo "[recipe-harness] compact build view - full log: $watch_log (mm-harness logs --full)"
|
|
231
238
|
started_pid=""
|
|
232
|
-
# Launch the watcher detached writing to the log; the tmux window is a read-only tail viewer.
|
|
233
239
|
path_prefix="${REPO_NODE_PATH_PREFIX:-}"
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
launcher="$SCRIPT_DIR/launch-webpack.cjs"
|
|
241
|
+
[ -f "$launcher" ] || { echo "launch-webpack.cjs missing from harness; run: mm-harness install" >&2; exit 1; }
|
|
242
|
+
sync_script="$SCRIPT_DIR/sync-webpack-dist.cjs"
|
|
243
|
+
[ -f "$sync_script" ] || { echo "sync-webpack-dist.cjs missing from harness; run: mm-harness install" >&2; exit 1; }
|
|
244
|
+
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}")
|
|
245
|
+
[ -z "$WATCHER_PORT" ] || launch_args+=(--port "$WATCHER_PORT")
|
|
246
|
+
[ -z "$path_prefix" ] || launch_args+=(--path-prefix "$path_prefix")
|
|
247
|
+
env -u BUNDLED_DEBUGPY_PATH node "$launcher" "${launch_args[@]}" >/dev/null
|
|
248
|
+
started_pid="$(cat "$watch_pid_file")"
|
|
237
249
|
# The orchestrator names the session; the harness only populates windows. Resolve
|
|
238
250
|
# via the run-owned ladder (RECIPE_TMUX_SESSION → agentic-runtime.json session) —
|
|
239
251
|
# no current-session fallback or slot-number derivation.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { stampRuntimeTitles } = require('./stamp-runtime-title.cjs');
|
|
8
|
+
|
|
9
|
+
const args = {};
|
|
10
|
+
for (let index = 2; index < process.argv.length; index += 1) {
|
|
11
|
+
const key = process.argv[index];
|
|
12
|
+
if (key === '--help' || key === '-h') {
|
|
13
|
+
process.stdout.write('Usage: sync-webpack-dist.cjs --watcher-pid <pid> --watch-log <path> --target <path> --dist <path> --runtime-dist <path> --pid-file <path>\n');
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
if (!key.startsWith('--') || index + 1 >= process.argv.length) throw new Error(`invalid argument: ${key}`);
|
|
17
|
+
args[key.slice(2)] = process.argv[++index];
|
|
18
|
+
}
|
|
19
|
+
for (const required of ['watcher-pid', 'watch-log', 'target', 'dist', 'runtime-dist', 'pid-file']) {
|
|
20
|
+
if (!args[required]) throw new Error(`--${required} is required`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const watcherPid = Number(args['watcher-pid']);
|
|
24
|
+
if (!Number.isInteger(watcherPid) || watcherPid <= 0) throw new Error('--watcher-pid must be a positive integer');
|
|
25
|
+
|
|
26
|
+
const watchLog = path.resolve(args['watch-log']);
|
|
27
|
+
const dist = path.resolve(args.dist);
|
|
28
|
+
const runtimeDist = path.resolve(args['runtime-dist']);
|
|
29
|
+
const pidFile = path.resolve(args['pid-file']);
|
|
30
|
+
const compilePattern = /compiled successfully|compiled with \d+ warning|MetaMask .* compiled|Bundle end: service worker|Bundle end:.*app-init/u;
|
|
31
|
+
let offset = 0;
|
|
32
|
+
let carry = '';
|
|
33
|
+
let timer;
|
|
34
|
+
|
|
35
|
+
function processAlive(pid) {
|
|
36
|
+
try {
|
|
37
|
+
process.kill(pid, 0);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function removeOwnPidFile() {
|
|
45
|
+
try {
|
|
46
|
+
if (fs.readFileSync(pidFile, 'utf8').trim() === String(process.pid)) fs.rmSync(pidFile, { force: true });
|
|
47
|
+
} catch {
|
|
48
|
+
// The launcher may have already removed or replaced it.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function syncRuntimeDist() {
|
|
53
|
+
if (!fs.existsSync(path.join(dist, 'manifest.json')) || !fs.existsSync(runtimeDist)) return;
|
|
54
|
+
const result = spawnSync('rsync', ['-a', '--exclude', '_metadata', `${dist}${path.sep}`, `${runtimeDist}${path.sep}`], {
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
});
|
|
57
|
+
if (result.status !== 0) {
|
|
58
|
+
process.stderr.write(`runtime-dist sync failed: ${(result.stderr || result.stdout || `rsync exited ${result.status}`).trim()}\n`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
stampRuntimeTitles({ target: path.resolve(args.target), runtimeDist });
|
|
62
|
+
process.stdout.write(`runtime-dist updated after webpack compile (${new Date().toISOString()})\n`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function poll() {
|
|
66
|
+
if (!processAlive(watcherPid)) {
|
|
67
|
+
removeOwnPidFile();
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const size = fs.statSync(watchLog).size;
|
|
72
|
+
if (size < offset) {
|
|
73
|
+
offset = 0;
|
|
74
|
+
carry = '';
|
|
75
|
+
}
|
|
76
|
+
if (size > offset) {
|
|
77
|
+
const length = size - offset;
|
|
78
|
+
const fd = fs.openSync(watchLog, 'r');
|
|
79
|
+
const chunk = Buffer.alloc(length);
|
|
80
|
+
fs.readSync(fd, chunk, 0, length, offset);
|
|
81
|
+
fs.closeSync(fd);
|
|
82
|
+
offset = size;
|
|
83
|
+
const text = carry + chunk.toString('utf8');
|
|
84
|
+
const lines = text.split(/\r?\n/u);
|
|
85
|
+
carry = lines.pop() || '';
|
|
86
|
+
if (lines.some((line) => compilePattern.test(line))) syncRuntimeDist();
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error?.code !== 'ENOENT') process.stderr.write(`runtime-dist sync monitor: ${error.message}\n`);
|
|
90
|
+
}
|
|
91
|
+
timer = setTimeout(poll, 250);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
|
|
95
|
+
process.on('SIGTERM', () => {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
removeOwnPidFile();
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
process.on('SIGINT', () => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
removeOwnPidFile();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
});
|
|
105
|
+
poll();
|
|
File without changes
|
package/adapters/manifest.json
CHANGED
|
@@ -18,6 +18,22 @@
|
|
|
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
|
+
},
|
|
21
37
|
{
|
|
22
38
|
"id": "mobile/launch-metro",
|
|
23
39
|
"entry": "adapters/mobile/launch-metro.cjs",
|
|
@@ -62,17 +78,17 @@
|
|
|
62
78
|
"id": "mobile/inject",
|
|
63
79
|
"entry": "adapters/mobile/inject.sh",
|
|
64
80
|
"kind": "bash",
|
|
65
|
-
"purpose": "Install
|
|
66
|
-
"inputs": "--target --
|
|
67
|
-
"outputs": "<harness>/manifest.json + runner/ + scripts
|
|
81
|
+
"purpose": "Install Mobile runner metadata, delegates, and snapshots without modifying product source.",
|
|
82
|
+
"inputs": "--target --no-git-exclude; env RECIPE_HARNESS_ROOT, METAMASK_RUNNER_PROTOCOL_ROOT",
|
|
83
|
+
"outputs": "<harness>/manifest.json + runner/ + scripts/; exit 0/1/2"
|
|
68
84
|
},
|
|
69
85
|
{
|
|
70
86
|
"id": "mobile/cleanup",
|
|
71
87
|
"entry": "adapters/mobile/cleanup.sh",
|
|
72
88
|
"kind": "bash",
|
|
73
|
-
"purpose": "Remove
|
|
89
|
+
"purpose": "Remove Mobile harness metadata and recorded git excludes without touching product source.",
|
|
74
90
|
"inputs": "--target --allow-managed-changes; env RECIPE_HARNESS_ROOT",
|
|
75
|
-
"outputs": "removes <harness
|
|
91
|
+
"outputs": "removes <harness>/; exit 0/1/2"
|
|
76
92
|
},
|
|
77
93
|
{
|
|
78
94
|
"id": "mobile/verify",
|
|
@@ -114,6 +130,30 @@
|
|
|
114
130
|
"inputs": "--target --runtime-dir --runner-bin --summary",
|
|
115
131
|
"outputs": "<runtime-dir>/recipe-harness-webpack.{pid,log}; optional summary; exit 0/1/2"
|
|
116
132
|
},
|
|
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
|
+
},
|
|
117
157
|
{
|
|
118
158
|
"id": "extension/stop-viewers",
|
|
119
159
|
"entry": "adapters/extension/stop-viewers.sh",
|
|
@@ -378,22 +418,6 @@
|
|
|
378
418
|
"inputs": "ESM import from resolve-slot-ports.mjs or resolve-slot-ports.ts",
|
|
379
419
|
"outputs": "resolveSlotPortsByRepo and related functions"
|
|
380
420
|
},
|
|
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
|
-
},
|
|
397
421
|
{
|
|
398
422
|
"id": "lib/recipe-harness-root",
|
|
399
423
|
"entry": "adapters/shared/recipe-harness-root.mjs",
|
|
@@ -406,18 +430,10 @@
|
|
|
406
430
|
"id": "lib/resolve-slot-ports-sh",
|
|
407
431
|
"entry": "adapters/shared/resolve-slot-ports.sh",
|
|
408
432
|
"kind": "lib",
|
|
409
|
-
"purpose": "
|
|
433
|
+
"purpose": "Sourced wrapper over resolve-slot-ports.mjs for shell runtime leaves.",
|
|
410
434
|
"inputs": "source + resolve_* / apply_resolved_* function calls",
|
|
411
435
|
"outputs": "KEY=VALUE lines on stdout; env vars via apply_resolved_*"
|
|
412
436
|
},
|
|
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
|
-
},
|
|
421
437
|
{
|
|
422
438
|
"id": "lib/reap-checkout-metros",
|
|
423
439
|
"entry": "adapters/shared/reap-checkout-metros.sh",
|
|
@@ -470,14 +486,6 @@
|
|
|
470
486
|
"purpose": "Open human-facing debugger UIs for extension CDP or mobile Metro runtimes.",
|
|
471
487
|
"inputs": "--adapter extension|mobile [--cdp-port|--port] [--target page|worker] [--json] [--no-open]",
|
|
472
488
|
"outputs": "debug URL/report on stdout; exit 0/1"
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
"id": "compat-overlays/mobile",
|
|
476
|
-
"entry": "adapters/mobile/overlay/compat",
|
|
477
|
-
"kind": "compat-overlay",
|
|
478
|
-
"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.",
|
|
479
|
-
"inputs": "manual `git apply` in the historical product checkout (no in-repo apply logic)",
|
|
480
|
-
"outputs": "patched historical checkout (reversible via git apply -R); overlay path recorded in eval evidence"
|
|
481
489
|
}
|
|
482
490
|
]
|
|
483
491
|
}
|
|
File without changes
|
|
@@ -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 built-in 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 —
|
|
@@ -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 Metro deviceName
|
|
139
|
+
// ANDROID_DEVICE — exact user-specified Metro deviceName.
|
|
140
140
|
const androidTargetName = loadAndroidTargetDeviceName();
|
|
141
141
|
const androidDevice = loadAndroidDevice();
|
|
142
142
|
const adbSerial = process.env.ADB_SERIAL || process.env.ANDROID_SERIAL || '';
|
|
@@ -205,8 +205,7 @@ async function discoverTarget(port) {
|
|
|
205
205
|
}
|
|
206
206
|
candidates = androidFiltered;
|
|
207
207
|
} else if (androidDevice) {
|
|
208
|
-
// Exact Metro deviceName match
|
|
209
|
-
// legacy path where ANDROID_DEVICE was not resolved from serial).
|
|
208
|
+
// Exact Metro deviceName match when the user set ANDROID_DEVICE directly.
|
|
210
209
|
androidFiltered = candidates.filter((t) => t.deviceName === androidDevice);
|
|
211
210
|
if (androidFiltered.length === 0) {
|
|
212
211
|
// Pinned by exact Metro name but no candidate matched — fail fast.
|
|
File without changes
|