@deeeed/metamask-harness 0.17.4 → 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 -103
- package/README.md +136 -96
- package/adapters/extension/console-tail.mjs +24 -55
- 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 -58
- package/adapters/extension/sync-webpack-dist.cjs +0 -107
- 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 -43
- 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
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Compatibility overlays (historical-commit replay)
|
|
2
|
+
|
|
3
|
+
NOT dead code. These overlays exist for replay/eval runs against OLD product
|
|
4
|
+
commits — e.g. re-running a historical task on a fixed baseline to validate
|
|
5
|
+
prompt or harness changes. A "zero current references" scan will always come
|
|
6
|
+
up empty here by design: the feature's purpose is non-current checkouts.
|
|
7
|
+
|
|
8
|
+
- Purpose: make historical product checkouts bootable/bridgeable under the
|
|
9
|
+
current local toolchain so Recipe v1 evals can replay against them.
|
|
10
|
+
- When it applies: mobile checkouts predating React Native 0.81 polyfill fixes.
|
|
11
|
+
- How it is applied: MANUALLY (no automatic apply logic in this repo).
|
|
12
|
+
The operator or the `/recipe-harness` skill applies the patch reversibly
|
|
13
|
+
before a historical rebuild (`git apply <patch>` in the product checkout,
|
|
14
|
+
`git apply -R` to remove) and records the overlay path in validation
|
|
15
|
+
evidence. `adapters/mobile/inject.sh` does NOT apply these.
|
|
16
|
+
|
|
17
|
+
Kept in the external runner repository, not Farmslot root scripts, so a
|
|
18
|
+
`/recipe-harness` skill can apply the same reversible overlay before a
|
|
19
|
+
historical rebuild and record the overlay path in validation evidence.
|
|
20
|
+
|
|
21
|
+
Listed in adapters/manifest.json (kind: compat-overlay) so the doctor
|
|
22
|
+
verifies the surface and this directory is never flagged as unused again.
|
|
23
|
+
|
|
24
|
+
## Patches
|
|
25
|
+
|
|
26
|
+
- `rn81-message-event-source.patch`: adds a read-only `MessageEvent.prototype.source`
|
|
27
|
+
getter for React Native 0.81 historical checkouts whose bundled polyfill omits
|
|
28
|
+
it while CDP websocket clients expect browser-compatible `MessageEvent` shape.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
compat-overlay: rn81-message-event-source (historical-checkout replay support)
|
|
2
|
+
|
|
3
|
+
What: patches metamask-mobile shim.js to add a read-only
|
|
4
|
+
MessageEvent.prototype.source getter.
|
|
5
|
+
Why: the harness CDP bridge expects browser-compatible MessageEvent shape;
|
|
6
|
+
pre-RN-0.81 checkouts ship a polyfill that omits .source, so the bridge
|
|
7
|
+
cannot attach when replaying tasks on those historical commits.
|
|
8
|
+
When: only when replaying/evaluating against mobile checkouts older than the
|
|
9
|
+
RN 0.81 upgrade (pre-RN-0.81 checkouts). Never needed on current main.
|
|
10
|
+
How: applied MANUALLY in the historical product checkout —
|
|
11
|
+
git apply <this file> (and `git apply -R` to remove)
|
|
12
|
+
No automatic apply logic exists in this repo. See ../README.md.
|
|
13
|
+
|
|
14
|
+
diff --git a/shim.js b/shim.js
|
|
15
|
+
--- a/shim.js
|
|
16
|
+
+++ b/shim.js
|
|
17
|
+
@@
|
|
18
|
+
if (typeof global.MessageEvent === 'undefined') {
|
|
19
|
+
global.MessageEvent =
|
|
20
|
+
require('react-native/src/private/webapis/html/events/MessageEvent').default;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
+// Historical runner compatibility: React Native 0.81's MessageEvent polyfill
|
|
24
|
+
+// can omit the browser `source` getter expected by CDP websocket clients under
|
|
25
|
+
+// newer Xcode/Hermes toolchains. Keep the getter read-only and null, matching
|
|
26
|
+
+// browser MessageEvent semantics for non-window messages.
|
|
27
|
+
+if (
|
|
28
|
+
+ global.MessageEvent?.prototype &&
|
|
29
|
+
+ !Object.getOwnPropertyDescriptor(global.MessageEvent.prototype, 'source')
|
|
30
|
+
+) {
|
|
31
|
+
+ Object.defineProperty(global.MessageEvent.prototype, 'source', {
|
|
32
|
+
+ configurable: true,
|
|
33
|
+
+ enumerable: true,
|
|
34
|
+
+ get() {
|
|
35
|
+
+ return null;
|
|
36
|
+
+ },
|
|
37
|
+
+ });
|
|
38
|
+
+}
|
|
39
|
+
+
|
|
40
|
+
class AbortError extends Error {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super(message);
|
|
@@ -77,6 +77,45 @@ default_metro_workers() {
|
|
|
77
77
|
# shellcheck disable=SC1091
|
|
78
78
|
. "$SCRIPT_DIR/lib/tmux-viewer.sh"
|
|
79
79
|
|
|
80
|
+
# Bridgeless RN gates console→Metro forwarding on console._isPolyfilled, which is
|
|
81
|
+
# false with the Hermes native console — device logs (incl. DevLogger) never reach
|
|
82
|
+
# metro.log. The forwarder attaches over CDP and appends them back. Kill switch:
|
|
83
|
+
# METAMASK_RECIPE_CONSOLE_FORWARD=0.
|
|
84
|
+
start_console_forwarder() {
|
|
85
|
+
local forwarder="$SCRIPT_DIR/bridge-runtime/console-forwarder.cjs"
|
|
86
|
+
local fwd_pid_file="$LOG_DIR/console-forwarder.pid"
|
|
87
|
+
[ "${METAMASK_RECIPE_CONSOLE_FORWARD:-1}" != "0" ] || return 0
|
|
88
|
+
{ [ -f "$forwarder" ] && command -v node >/dev/null 2>&1; } || return 0
|
|
89
|
+
# Only one debugger can own a React Native page. Reap any forwarder already
|
|
90
|
+
# attached to this Metro port, even if it was launched from another checkout or
|
|
91
|
+
# package install path and therefore does not own this runtime dir's pid file.
|
|
92
|
+
local pid cmd
|
|
93
|
+
while read -r pid cmd; do
|
|
94
|
+
case "$cmd" in
|
|
95
|
+
*console-forwarder.cjs*" --port $PORT "*|*console-forwarder.cjs*" --port=$PORT "*)
|
|
96
|
+
kill "$pid" 2>/dev/null || true
|
|
97
|
+
;;
|
|
98
|
+
esac
|
|
99
|
+
done < <(ps -axo pid=,command= 2>/dev/null || true)
|
|
100
|
+
if [ -f "$fwd_pid_file" ]; then
|
|
101
|
+
local old_fwd
|
|
102
|
+
old_fwd="$(cat "$fwd_pid_file" 2>/dev/null || true)"
|
|
103
|
+
# A crash can leave a stale pid file and the OS can recycle the pid for an
|
|
104
|
+
# unrelated process — only kill when the command line is really ours.
|
|
105
|
+
if [ -n "$old_fwd" ]; then
|
|
106
|
+
case "$(ps -p "$old_fwd" -o command= 2>/dev/null)" in
|
|
107
|
+
*console-forwarder.cjs*) kill "$old_fwd" 2>/dev/null ;;
|
|
108
|
+
esac
|
|
109
|
+
fi
|
|
110
|
+
rm -f "$fwd_pid_file"
|
|
111
|
+
fi
|
|
112
|
+
nohup node "$forwarder" --port "$PORT" --out "$LOG_FILE" \
|
|
113
|
+
</dev/null >> "$LOG_DIR/console-forwarder.err" 2>&1 &
|
|
114
|
+
echo "$!" > "$fwd_pid_file"
|
|
115
|
+
disown 2>/dev/null || true
|
|
116
|
+
printf 'Device console → metro.log (CDP forwarder pid %s)\n' "$(cat "$fwd_pid_file")" >&2
|
|
117
|
+
}
|
|
118
|
+
|
|
80
119
|
write_metro_runner() {
|
|
81
120
|
local runner="$1"
|
|
82
121
|
local clear_flag=""
|
|
@@ -140,6 +179,7 @@ if metro_ready; then
|
|
|
140
179
|
rm -f "$PID_FILE"
|
|
141
180
|
elif metro_listener_valid; then
|
|
142
181
|
printf 'Metro already running and valid on port %s\n' "$PORT" >&2
|
|
182
|
+
start_console_forwarder
|
|
143
183
|
exit 0
|
|
144
184
|
else
|
|
145
185
|
stop_metro_listener || exit 1
|
|
@@ -229,6 +269,7 @@ if [ "$READY" = true ]; then
|
|
|
229
269
|
first_ready_pid="${ready_pids%% *}"
|
|
230
270
|
[ -z "$first_ready_pid" ] || printf '%s\n' "$first_ready_pid" > "$PID_FILE"
|
|
231
271
|
printf 'Metro ready on port %s\n' "$PORT" >&2
|
|
272
|
+
start_console_forwarder
|
|
232
273
|
printf ' Next: mm-harness logs (tail Metro)\n' >&2
|
|
233
274
|
exit 0
|
|
234
275
|
fi
|
|
@@ -261,19 +261,76 @@ check_file "package.json"
|
|
|
261
261
|
check_file "app/dev-tools/AgenticService/AgenticService.ts"
|
|
262
262
|
|
|
263
263
|
if ! grep -q "AgenticService.install" "$TARGET/app/core/NavigationService/NavigationService.ts" 2>/dev/null; then
|
|
264
|
-
checks+=("{\"name\":\"NavigationService
|
|
264
|
+
checks+=("{\"name\":\"NavigationService patch\",\"status\":\"fail\"}")
|
|
265
265
|
status="fail"
|
|
266
266
|
else
|
|
267
|
-
checks+=("{\"name\":\"NavigationService
|
|
267
|
+
checks+=("{\"name\":\"NavigationService patch\",\"status\":\"pass\"}")
|
|
268
268
|
fi
|
|
269
269
|
|
|
270
270
|
if ! grep -q "AgentStepHud" "$TARGET/app/components/Nav/App/App.tsx" 2>/dev/null; then
|
|
271
|
-
checks+=("{\"name\":\"App AgentStepHud\",\"status\":\"fail\"}")
|
|
271
|
+
checks+=("{\"name\":\"App AgentStepHud patch\",\"status\":\"fail\"}")
|
|
272
272
|
status="fail"
|
|
273
273
|
else
|
|
274
|
-
checks+=("{\"name\":\"App AgentStepHud\",\"status\":\"pass\"}")
|
|
274
|
+
checks+=("{\"name\":\"App AgentStepHud patch\",\"status\":\"pass\"}")
|
|
275
275
|
fi
|
|
276
276
|
|
|
277
|
+
# Drift detection (read-only): compare the in-repo AgenticService/HUD against
|
|
278
|
+
# the resolved runner overlay, not a runner-owned copy. The runner is the only
|
|
279
|
+
# MetaMask-aware source of truth for Mobile injection.
|
|
280
|
+
overlay_drift_check="$(
|
|
281
|
+
HARNESS_DIR_FOR_DRIFT="$HARNESS_DIR" TARGET_FOR_DRIFT="$TARGET" node <<'NODE'
|
|
282
|
+
const fs = require('fs');
|
|
283
|
+
const path = require('path');
|
|
284
|
+
const name = 'agentic overlay matches runner (HUD freshness)';
|
|
285
|
+
const harnessDir = process.env.HARNESS_DIR_FOR_DRIFT;
|
|
286
|
+
const targetDir = path.join(process.env.TARGET_FOR_DRIFT, 'app', 'dev-tools', 'AgenticService');
|
|
287
|
+
function readTrimmed(file) {
|
|
288
|
+
try { return fs.readFileSync(file, 'utf8').trim(); } catch { return ''; }
|
|
289
|
+
}
|
|
290
|
+
let runnerDir = readTrimmed(path.join(harnessDir, 'runner', '.runner-source'));
|
|
291
|
+
if (!runnerDir) {
|
|
292
|
+
try {
|
|
293
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(harnessDir, 'manifest.json'), 'utf8'));
|
|
294
|
+
runnerDir = manifest && manifest.source && manifest.source.runnerDir || '';
|
|
295
|
+
} catch {}
|
|
296
|
+
}
|
|
297
|
+
if (!runnerDir) {
|
|
298
|
+
process.stdout.write(JSON.stringify({ name, status: 'warn', detail: 'runner source unavailable; overlay freshness compare skipped' }));
|
|
299
|
+
process.exit(0);
|
|
300
|
+
}
|
|
301
|
+
const overlayDir = path.join(runnerDir, 'adapters', 'mobile', 'overlay', 'app', 'dev-tools', 'AgenticService');
|
|
302
|
+
let checked = 0;
|
|
303
|
+
const drifted = [];
|
|
304
|
+
const missing = [];
|
|
305
|
+
try {
|
|
306
|
+
for (const entry of fs.readdirSync(overlayDir)) {
|
|
307
|
+
if (!entry.endsWith('.patch')) continue;
|
|
308
|
+
if (/\.test\./u.test(entry)) continue;
|
|
309
|
+
const base = entry.slice(0, -'.patch'.length);
|
|
310
|
+
const targetFile = path.join(targetDir, base);
|
|
311
|
+
if (!fs.existsSync(targetFile)) { missing.push(base); continue; }
|
|
312
|
+
checked += 1;
|
|
313
|
+
if (fs.readFileSync(path.join(overlayDir, entry), 'utf8') !== fs.readFileSync(targetFile, 'utf8')) {
|
|
314
|
+
drifted.push(base);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
process.stdout.write(JSON.stringify({ name, status: 'warn', detail: 'runner overlay compare skipped: ' + String((error && error.message) || error) }));
|
|
319
|
+
process.exit(0);
|
|
320
|
+
}
|
|
321
|
+
if (drifted.length || missing.length) {
|
|
322
|
+
const parts = [];
|
|
323
|
+
if (drifted.length) parts.push('behind runner: ' + drifted.join(', '));
|
|
324
|
+
if (missing.length) parts.push('absent in repo: ' + missing.join(', '));
|
|
325
|
+
process.stdout.write(JSON.stringify({ name, status: 'warn', detail: parts.join('; ') + ' — rerun mm-harness install --force-overlay or mm-harness fixtures sync to refresh the in-repo HUD/AgenticService from the runner' }));
|
|
326
|
+
} else if (checked === 0) {
|
|
327
|
+
process.stdout.write(JSON.stringify({ name, status: 'pass' }));
|
|
328
|
+
} else {
|
|
329
|
+
process.stdout.write(JSON.stringify({ name, status: 'pass' }));
|
|
330
|
+
}
|
|
331
|
+
NODE
|
|
332
|
+
)"
|
|
333
|
+
checks+=("$overlay_drift_check")
|
|
277
334
|
run_with_timeout() {
|
|
278
335
|
local log_path="$1"
|
|
279
336
|
local timeout_s="$2"
|
|
@@ -24,10 +24,10 @@ REQUIRE_PLATFORM=""
|
|
|
24
24
|
|
|
25
25
|
while [ "$#" -gt 0 ]; do
|
|
26
26
|
case "$1" in
|
|
27
|
-
--target)
|
|
28
|
-
--port)
|
|
29
|
-
--max-polls)
|
|
30
|
-
--platform)
|
|
27
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
28
|
+
--port) PORT="$2"; shift 2 ;;
|
|
29
|
+
--max-polls) MAX_POLLS="$2"; shift 2 ;;
|
|
30
|
+
--platform) REQUIRE_PLATFORM="$2"; shift 2 ;;
|
|
31
31
|
-h|--help)
|
|
32
32
|
printf 'Usage: wait-for-bridge.sh [--target <dir>] [--port <n>] [--max-polls <n>] [--platform ios|android]\n'
|
|
33
33
|
exit 0
|
|
@@ -36,12 +36,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
36
36
|
esac
|
|
37
37
|
done
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
''|*[!0-9]*) echo "wait-for-bridge: --max-polls must be an integer of at least 2." >&2; exit 2 ;;
|
|
41
|
-
esac
|
|
42
|
-
[ "$MAX_POLLS" -ge 2 ] || { echo "wait-for-bridge: --max-polls must be at least 2 for stable readiness." >&2; exit 2; }
|
|
43
|
-
|
|
44
|
-
TARGET="$(cd "$TARGET" && pwd -P)"
|
|
39
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
45
40
|
|
|
46
41
|
# The matcher reads the required platform from the environment so the value survives
|
|
47
42
|
# into the node subprocess that runs the shared matcher against the status output.
|
|
@@ -133,24 +128,15 @@ try {
|
|
|
133
128
|
' "$MATCH_LIB" "$STATUS_LOG" 2>/dev/null || printf 'none'
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
READY_STREAK=0
|
|
137
131
|
for ATTEMPT in $(seq 1 "$MAX_POLLS"); do
|
|
138
132
|
if bridge_has_route; then
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# startup navigation/reload. Require two consecutive healthy snapshots so a
|
|
142
|
-
# following command never inherits that transient gap.
|
|
143
|
-
if [ "$READY_STREAK" -ge 2 ]; then
|
|
144
|
-
printf 'Mobile bridge ready\n' >&2
|
|
145
|
-
exit 0
|
|
146
|
-
fi
|
|
147
|
-
else
|
|
148
|
-
READY_STREAK=0
|
|
133
|
+
printf 'Mobile bridge ready\n' >&2
|
|
134
|
+
exit 0
|
|
149
135
|
fi
|
|
150
136
|
if [ "$ATTEMPT" = "1" ] || [ $((ATTEMPT % 5)) -eq 0 ]; then
|
|
151
137
|
printf 'Waiting for Mobile bridge (%s/%s): %s\n' "$ATTEMPT" "$MAX_POLLS" "$(bridge_wait_reason)" >&2
|
|
152
138
|
fi
|
|
153
|
-
|
|
139
|
+
sleep 2
|
|
154
140
|
done
|
|
155
141
|
|
|
156
142
|
cat "$STATUS_LOG" >&2 || true
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// harness-source-fingerprint.mjs — deterministic fingerprint of the runner files
|
|
2
|
+
// that get injected into a slot's harness overlay.
|
|
3
|
+
//
|
|
4
|
+
// inject.mjs stamps this into the slot manifest at install time; the launch-time
|
|
5
|
+
// freshness guard recomputes it and re-injects when they differ, so a slot can
|
|
6
|
+
// never silently run a stale injected launcher after the runner changes. This is
|
|
7
|
+
// what makes the runner the single source of truth: edit a runner adapter file and
|
|
8
|
+
// the next launch refreshes every slot from it.
|
|
9
|
+
//
|
|
10
|
+
// stat-only (size + mtime) so it is cheap enough to run on every launch; no file
|
|
11
|
+
// contents are read and no Date.now()/random is used (both would break determinism).
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import crypto from 'node:crypto';
|
|
15
|
+
|
|
16
|
+
// Trees whose contents are copied into <slot>/harness by inject.mjs. Any change
|
|
17
|
+
// here means injected slots are stale.
|
|
18
|
+
const SOURCE_DIRS = ['adapters/extension', 'adapters/shared', 'library'];
|
|
19
|
+
|
|
20
|
+
export function harnessSourceFingerprint(runnerDir) {
|
|
21
|
+
const entries = [];
|
|
22
|
+
for (const rel of SOURCE_DIRS) {
|
|
23
|
+
collect(path.join(runnerDir, rel), runnerDir, entries);
|
|
24
|
+
}
|
|
25
|
+
entries.sort();
|
|
26
|
+
return crypto.createHash('sha256').update(entries.join('\n')).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function collect(dir, root, out) {
|
|
30
|
+
let dirents;
|
|
31
|
+
try {
|
|
32
|
+
dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
33
|
+
} catch {
|
|
34
|
+
return; // absent tree contributes nothing
|
|
35
|
+
}
|
|
36
|
+
for (const dirent of dirents) {
|
|
37
|
+
// Skip hidden noise (.DS_Store, editor swap files) so the fingerprint tracks
|
|
38
|
+
// only real source and does not thrash re-injects.
|
|
39
|
+
if (dirent.name.startsWith('.')) continue;
|
|
40
|
+
const full = path.join(dir, dirent.name);
|
|
41
|
+
if (dirent.isDirectory()) {
|
|
42
|
+
collect(full, root, out);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (!dirent.isFile()) continue;
|
|
46
|
+
const stat = fs.statSync(full);
|
|
47
|
+
out.push(`${path.relative(root, full)}|${stat.size}|${Math.floor(stat.mtimeMs)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -4,11 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
TARGET=""
|
|
5
5
|
while [[ $# -gt 0 ]]; do
|
|
6
6
|
case "$1" in
|
|
7
|
-
--target)
|
|
8
|
-
[[ $# -ge 2 ]] || { echo "Usage: install-repo-deps.sh --target <checkout>" >&2; exit 2; }
|
|
9
|
-
TARGET="$2"
|
|
10
|
-
shift 2
|
|
11
|
-
;;
|
|
7
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
12
8
|
-h|--help)
|
|
13
9
|
echo "Usage: install-repo-deps.sh --target <checkout>"
|
|
14
10
|
exit 0
|