@deeeed/metamask-harness 0.17.5 → 0.19.0
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 +1059 -0
- package/README.md +91 -140
- package/adapters/core/inject.sh +1 -6
- package/adapters/extension/console-tail.mjs +55 -24
- package/adapters/extension/ensure-browser.sh +6 -1
- package/adapters/extension/inject.mjs +7 -13
- package/adapters/extension/launch-browser.cjs +7 -1
- package/adapters/extension/launch-webpack.cjs +83 -0
- package/adapters/extension/launch.sh +8 -11
- package/adapters/extension/lib/chrome-args.cjs +8 -1
- package/adapters/extension/lib/macos-focus.cjs +32 -0
- package/adapters/extension/live.sh +12 -21
- package/adapters/extension/reattach.sh +1 -0
- package/adapters/extension/seed-fixture.sh +4 -12
- package/adapters/extension/sidepanel-toggle.sh +4 -1
- package/adapters/extension/stamp-runtime-title.cjs +58 -0
- package/adapters/extension/start-watch.sh +17 -4
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/sync-webpack-dist.cjs +107 -0
- package/adapters/extension/verify.sh +2 -2
- package/adapters/manifest.json +53 -37
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +16 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +3 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
- package/adapters/mobile/cleanup.sh +16 -176
- package/adapters/mobile/inject.sh +91 -671
- package/adapters/mobile/launch-console-forwarder.cjs +39 -0
- package/adapters/mobile/open-device.sh +45 -7
- package/adapters/mobile/start-console-forwarder.sh +70 -0
- package/adapters/mobile/start-metro.sh +0 -41
- package/adapters/mobile/verify.sh +34 -83
- package/adapters/mobile/wait-for-bridge.sh +22 -8
- package/adapters/shared/harness-source-fingerprint.mjs +14 -12
- package/adapters/shared/resolve-slot-ports-core.mjs +17 -6
- package/adapters/shared/resolve-slot-ports.sh +17 -5
- package/adapters/shared/update-check-worker.mjs +43 -0
- package/bin/mm-harness +21 -2
- package/dist/adapters/core/surface.js +4 -1
- package/dist/adapters/extension/console-capture.js +95 -0
- package/dist/adapters/extension/product-config.js +110 -0
- package/dist/adapters/extension/runtime-decision.js +20 -2
- package/dist/adapters/extension/surface.js +22 -3
- package/dist/adapters/harness-freshness.js +49 -0
- package/dist/adapters/mobile/perps-env.js +43 -3
- package/dist/adapters/mobile/prepare.js +39 -7
- package/dist/adapters/mobile/surface.js +5 -2
- package/dist/adapters/resolve-slot-ports.js +2 -2
- package/dist/adapters/slot-ports.js +13 -16
- package/dist/adapters.js +56 -16
- package/dist/checkout-lock.js +27 -2
- package/dist/cli-color.js +19 -0
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +7 -18
- package/dist/command-contract.js +462 -0
- package/dist/command-journal.js +263 -0
- package/dist/commands/call.js +252 -57
- package/dist/commands/check.js +10 -4
- package/dist/commands/completion-candidates.js +17 -22
- package/dist/commands/device-target.js +27 -12
- package/dist/commands/doctor.js +106 -25
- package/dist/commands/fixtures.js +142 -35
- package/dist/commands/last.js +60 -0
- package/dist/commands/launch/extension.js +40 -17
- package/dist/commands/launch/index.js +207 -63
- package/dist/commands/launch/mobile.js +2 -0
- package/dist/commands/list-executables.js +187 -29
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +286 -35
- package/dist/commands/parse-args.js +68 -4
- package/dist/commands/provision.js +10 -4
- package/dist/commands/run-engine.js +772 -264
- package/dist/commands/run-report.js +12 -3
- package/dist/commands/run.js +378 -85
- package/dist/commands/shared.js +76 -4
- package/dist/commands/status-probe.js +4 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/stop.js +7 -2
- package/dist/commands/update.js +16 -28
- package/dist/completions-cache.js +1 -1
- package/dist/doctor.js +57 -10
- package/dist/harness.js +47 -137
- package/dist/heal-bounds.js +7 -2
- package/dist/json-stream.js +57 -0
- package/dist/live-adapter-contract.js +138 -32
- package/dist/manifest.js +161 -1
- package/dist/mm-harness-cli.js +146 -73
- package/dist/paths.js +2 -5
- package/dist/recipe-security.js +178 -0
- package/dist/run-diagnostics.js +261 -0
- package/dist/run-recording.js +1 -1
- package/dist/runner.js +140 -9
- package/docs/CONTRIBUTING.md +136 -0
- package/docs/QA.md +184 -0
- package/docs/RECIPES.md +122 -0
- package/docs/SECURITY.md +88 -0
- package/library/README.md +7 -3
- package/library/actions/core/perps/_controller.mjs +43 -56
- package/library/actions/core/perps/assert_orders.mjs +6 -7
- package/library/actions/core/perps/assert_positions.mjs +6 -7
- package/library/actions/core/perps/close_orders.mjs +2 -0
- package/library/actions/core/perps/close_positions.mjs +2 -0
- package/library/actions/core/perps/ensure_orders.mjs +4 -2
- package/library/actions/core/perps/ensure_positions.mjs +4 -2
- package/library/actions/core/perps/place_order.mjs +7 -3
- package/library/actions/core/perps/read_account.mjs +2 -2
- package/library/actions/core/perps/read_orders.mjs +2 -1
- package/library/actions/core/perps/read_positions.mjs +2 -1
- package/library/actions/core/wallet/list_accounts.mjs +95 -0
- package/library/actions/extension/perps/assert_orders.mjs +2 -1
- package/library/actions/extension/perps/assert_positions.mjs +2 -1
- package/library/actions/extension/perps/perps.mjs +43 -14
- package/library/actions/extension/platform/cdp.mjs +1 -0
- package/library/actions/extension/wallet/list_accounts.mjs +41 -0
- package/library/actions/mobile/perps/assert_orders.mjs +2 -1
- package/library/actions/mobile/perps/assert_positions.mjs +2 -1
- package/library/actions/mobile/perps/perps.mjs +40 -12
- package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
- package/library/library.json +1 -1
- package/library/manifests/core.action-manifest.json +1214 -396
- package/library/manifests/extension.action-manifest.json +1635 -728
- package/library/manifests/mobile.action-manifest.json +1740 -727
- package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
- package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
- package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
- package/library/recipes/perps/lifecycle.recipe.json +136 -180
- package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
- package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
- package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
- package/library/recipes/perps/performance.mobile.recipe.json +37 -51
- package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
- package/library/recipes/perps/read-markets.core.recipe.json +29 -31
- package/library/recipes/perps/smoke.core.recipe.json +29 -32
- package/library/recipes/perps/smoke.extension.recipe.json +41 -44
- package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
- package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
- package/library/recipes/runner/action-validation.extension.recipe.json +312 -398
- package/library/recipes/runner/action-validation.mobile.recipe.json +316 -402
- package/library/recipes/runner/smoke.core.recipe.json +25 -0
- package/library/recipes/runner/smoke.extension.recipe.json +23 -24
- package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
- package/library/recipes/wallet/smoke.extension.recipe.json +40 -0
- package/library/recipes/wallet/smoke.mobile.recipe.json +40 -0
- package/package.json +12 -5
- package/scripts/completions.sh +8 -11
- package/scripts/validate-human-outcomes.mjs +169 -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/dist/adapters/extension/harness-freshness.js +0 -39
- package/dist/commands/flows.js +0 -62
- package/docs/ADAPTER-SURFACE.md +0 -119
- package/docs/CHEATSHEET.md +0 -61
- package/docs/CLI-SPEC.md +0 -1098
- package/docs/CODE-MAP.md +0 -62
- package/docs/DEBUG-HANDOVER.md +0 -36
- package/docs/MENTAL-MODEL.md +0 -295
- package/docs/UX-PRINCIPLES.md +0 -64
- package/docs/architecture.md +0 -398
- package/docs/live-adapter-contract.md +0 -188
- package/docs/package-boundaries.md +0 -47
- package/docs/perps-flow-catalog.md +0 -235
- package/docs/recipe-libraries.md +0 -301
- package/docs/runtime-file-conventions.md +0 -36
- package/library/flows/perps.flows.json +0 -64
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { spawn } = require('node:child_process');
|
|
7
|
+
|
|
8
|
+
const args = { port: '', out: '', err: '' };
|
|
9
|
+
for (let i = 2; i < process.argv.length; i += 1) {
|
|
10
|
+
const key = process.argv[i];
|
|
11
|
+
if (key === '--port') args.port = process.argv[++i] || '';
|
|
12
|
+
else if (key === '--out') args.out = process.argv[++i] || '';
|
|
13
|
+
else if (key === '--err') args.err = process.argv[++i] || '';
|
|
14
|
+
else if (key === '--help' || key === '-h') {
|
|
15
|
+
process.stdout.write('Usage: launch-console-forwarder.cjs --port <port> --out <app-log> --err <error-log>\n');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
} else {
|
|
18
|
+
process.stderr.write(`launch-console-forwarder: unknown arg: ${key}\n`);
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!args.port || !args.out || !args.err) {
|
|
24
|
+
process.stderr.write('launch-console-forwarder: --port, --out, and --err are required\n');
|
|
25
|
+
process.exit(2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const forwarder = path.join(__dirname, 'bridge-runtime', 'console-forwarder.cjs');
|
|
29
|
+
const errFd = fs.openSync(args.err, 'a');
|
|
30
|
+
try {
|
|
31
|
+
const child = spawn(process.execPath, [forwarder, '--port', args.port, '--out', args.out], {
|
|
32
|
+
detached: true,
|
|
33
|
+
stdio: ['ignore', errFd, errFd],
|
|
34
|
+
});
|
|
35
|
+
child.unref();
|
|
36
|
+
process.stdout.write(`${child.pid}\n`);
|
|
37
|
+
} finally {
|
|
38
|
+
fs.closeSync(errFd);
|
|
39
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# --simulator <udid|name|"booted"> iOS simulator (default: IOS_SIMULATOR env, else "booted")
|
|
14
14
|
# --adb-serial <serial> Android device serial (default: ADB_SERIAL env)
|
|
15
15
|
# --preflight-mode fast|auto|rebuild-native|clean (default: fast; env MOBILE_PREFLIGHT_MODE)
|
|
16
|
+
# --ui-only iOS only: ensure Simulator.app is visible; do not relaunch the app
|
|
16
17
|
# Outputs:
|
|
17
18
|
# Progress on stderr; exit 0 pass; 1 open failed; 2 bad args.
|
|
18
19
|
#
|
|
@@ -25,6 +26,7 @@ SIMULATOR="${IOS_SIMULATOR:-${SIM_UDID:-booted}}"
|
|
|
25
26
|
ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
|
|
26
27
|
PREFLIGHT_MODE="${MOBILE_PREFLIGHT_MODE:-fast}"
|
|
27
28
|
PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
|
|
29
|
+
UI_ONLY=false
|
|
28
30
|
|
|
29
31
|
while [ "$#" -gt 0 ]; do
|
|
30
32
|
case "$1" in
|
|
@@ -34,10 +36,11 @@ while [ "$#" -gt 0 ]; do
|
|
|
34
36
|
--adb-serial) ADB_SERIAL_ARG="$2"; shift 2 ;;
|
|
35
37
|
--preflight-mode) PREFLIGHT_MODE="$2"; shift 2 ;;
|
|
36
38
|
--port) PORT="$2"; shift 2 ;;
|
|
39
|
+
--ui-only) UI_ONLY=true; shift ;;
|
|
37
40
|
-h|--help)
|
|
38
41
|
printf 'Usage: open-device.sh [--platform ios|android] [--target <dir>]\n'
|
|
39
42
|
printf ' [--simulator <udid|name|booted>] [--adb-serial <serial>]\n'
|
|
40
|
-
printf ' [--preflight-mode fast|auto|rebuild-native|clean] [--port <n>]\n'
|
|
43
|
+
printf ' [--preflight-mode fast|auto|rebuild-native|clean] [--port <n>] [--ui-only]\n'
|
|
41
44
|
exit 0
|
|
42
45
|
;;
|
|
43
46
|
*) printf 'open-device: unknown arg: %s\n' "$1" >&2; exit 2 ;;
|
|
@@ -48,6 +51,10 @@ case "$PLATFORM" in
|
|
|
48
51
|
ios|android) ;;
|
|
49
52
|
*) printf 'open-device: --platform must be ios or android (got: %s)\n' "$PLATFORM" >&2; exit 2 ;;
|
|
50
53
|
esac
|
|
54
|
+
if $UI_ONLY && [ "$PLATFORM" != "ios" ]; then
|
|
55
|
+
printf 'open-device: --ui-only is supported only for ios.\n' >&2
|
|
56
|
+
exit 2
|
|
57
|
+
fi
|
|
51
58
|
|
|
52
59
|
TARGET="$(cd "$TARGET" && pwd)"
|
|
53
60
|
|
|
@@ -149,19 +156,46 @@ boot_simulator_if_needed() {
|
|
|
149
156
|
xcrun simctl bootstatus "$udid" -b >/dev/null
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
|
|
159
|
+
simulator_ui_running() {
|
|
160
|
+
pgrep -x Simulator >/dev/null 2>&1
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
open_simulator_in_background_if_needed() {
|
|
153
164
|
local target="$1" udid=""
|
|
165
|
+
[ "${MM_HARNESS_SHOW_SIMULATOR:-1}" != "0" ] || return 0
|
|
166
|
+
simulator_ui_running && return 0
|
|
167
|
+
printf 'Opening Simulator UI in the background\n' >&2
|
|
154
168
|
if [ "$target" = "booted" ]; then
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
open -g -a Simulator >/dev/null 2>&1 || {
|
|
170
|
+
printf 'open-device: failed to open Simulator UI.\n' >&2
|
|
171
|
+
return 1
|
|
172
|
+
}
|
|
157
173
|
else
|
|
158
174
|
udid="$(sim_udid_for_target "$target")"
|
|
159
175
|
if [ -n "$udid" ]; then
|
|
160
|
-
open -a Simulator --args -CurrentDeviceUDID "$udid" >/dev/null 2>&1 ||
|
|
176
|
+
open -g -a Simulator --args -CurrentDeviceUDID "$udid" >/dev/null 2>&1 || {
|
|
177
|
+
printf 'open-device: failed to open Simulator UI for %s.\n' "$target" >&2
|
|
178
|
+
return 1
|
|
179
|
+
}
|
|
161
180
|
else
|
|
162
|
-
open -a Simulator >/dev/null 2>&1 ||
|
|
181
|
+
open -g -a Simulator >/dev/null 2>&1 || {
|
|
182
|
+
printf 'open-device: failed to open Simulator UI.\n' >&2
|
|
183
|
+
return 1
|
|
184
|
+
}
|
|
163
185
|
fi
|
|
164
186
|
fi
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
focus_simulator() {
|
|
190
|
+
local target="$1" udid=""
|
|
191
|
+
if [ "$target" != "booted" ]; then
|
|
192
|
+
udid="$(sim_udid_for_target "$target")"
|
|
193
|
+
fi
|
|
194
|
+
if [ -n "$udid" ]; then
|
|
195
|
+
open -a Simulator --args -CurrentDeviceUDID "$udid" >/dev/null 2>&1 || true
|
|
196
|
+
else
|
|
197
|
+
open -a Simulator >/dev/null 2>&1 || true
|
|
198
|
+
fi
|
|
165
199
|
osascript -e 'tell application "Simulator" to activate' >/dev/null 2>&1 || true
|
|
166
200
|
}
|
|
167
201
|
|
|
@@ -220,8 +254,12 @@ if [ "$PLATFORM" = "ios" ]; then
|
|
|
220
254
|
DEV_CLIENT_SCHEME="${IOS_DEV_CLIENT_SCHEME:-expo-metamask}"
|
|
221
255
|
|
|
222
256
|
boot_simulator_if_needed "$SIM_TARGET" || exit 1
|
|
257
|
+
open_simulator_in_background_if_needed "$SIM_TARGET" || exit 1
|
|
223
258
|
if [ "${MM_HARNESS_FOCUS_SIMULATOR:-0}" = "1" ]; then
|
|
224
|
-
|
|
259
|
+
focus_simulator "$SIM_TARGET"
|
|
260
|
+
fi
|
|
261
|
+
if $UI_ONLY; then
|
|
262
|
+
exit 0
|
|
225
263
|
fi
|
|
226
264
|
|
|
227
265
|
LAUNCHED=false
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Ensure one runner-owned CDP console forwarder is alive for this Mobile slot.
|
|
3
|
+
set -uo pipefail
|
|
4
|
+
|
|
5
|
+
TARGET="$PWD"
|
|
6
|
+
PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
|
|
7
|
+
QUIET=false
|
|
8
|
+
|
|
9
|
+
while [ "$#" -gt 0 ]; do
|
|
10
|
+
case "$1" in
|
|
11
|
+
--target) [ "$#" -ge 2 ] || { echo "Missing value for --target" >&2; exit 2; }; TARGET="$2"; shift 2 ;;
|
|
12
|
+
--port) [ "$#" -ge 2 ] || { echo "Missing value for --port" >&2; exit 2; }; PORT="$2"; shift 2 ;;
|
|
13
|
+
--quiet) QUIET=true; shift ;;
|
|
14
|
+
-h|--help)
|
|
15
|
+
printf 'Usage: start-console-forwarder.sh [--target <dir>] [--port <port>] [--quiet]\n'
|
|
16
|
+
exit 0
|
|
17
|
+
;;
|
|
18
|
+
*) printf 'start-console-forwarder: unknown arg: %s\n' "$1" >&2; exit 2 ;;
|
|
19
|
+
esac
|
|
20
|
+
done
|
|
21
|
+
|
|
22
|
+
[ "${METAMASK_RECIPE_CONSOLE_FORWARD:-1}" != "0" ] || exit 0
|
|
23
|
+
TARGET="$(cd "$TARGET" && pwd -P)"
|
|
24
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
25
|
+
# shellcheck disable=SC1091
|
|
26
|
+
. "$SCRIPT_DIR/../shared/harness-path.sh"
|
|
27
|
+
LOG_DIR="$TARGET/$(recipe_runtime_dir)" || exit 1
|
|
28
|
+
FORWARDER="$SCRIPT_DIR/bridge-runtime/console-forwarder.cjs"
|
|
29
|
+
PID_FILE="$LOG_DIR/console-forwarder.pid"
|
|
30
|
+
APP_LOG_FILE="$LOG_DIR/app-console.log"
|
|
31
|
+
|
|
32
|
+
{ [ -f "$FORWARDER" ] && command -v node >/dev/null 2>&1; } || exit 0
|
|
33
|
+
mkdir -p "$LOG_DIR"
|
|
34
|
+
|
|
35
|
+
# One debugger owns a React Native page. Reap any collector for this Metro port,
|
|
36
|
+
# including one left by an older globally installed harness.
|
|
37
|
+
while read -r pid cmd; do
|
|
38
|
+
case "$cmd" in
|
|
39
|
+
*console-forwarder.cjs*" --port $PORT "*|*console-forwarder.cjs*" --port=$PORT "*)
|
|
40
|
+
kill "$pid" 2>/dev/null || true
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
done < <(ps -axo pid=,command= 2>/dev/null || true)
|
|
44
|
+
|
|
45
|
+
if [ -f "$PID_FILE" ]; then
|
|
46
|
+
old_pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
47
|
+
if [ -n "$old_pid" ]; then
|
|
48
|
+
case "$(ps -p "$old_pid" -o command= 2>/dev/null)" in
|
|
49
|
+
*console-forwarder.cjs*) kill "$old_pid" 2>/dev/null || true ;;
|
|
50
|
+
esac
|
|
51
|
+
fi
|
|
52
|
+
rm -f "$PID_FILE"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
: > "$APP_LOG_FILE"
|
|
56
|
+
pid="$(node "$SCRIPT_DIR/launch-console-forwarder.cjs" \
|
|
57
|
+
--port "$PORT" \
|
|
58
|
+
--out "$APP_LOG_FILE" \
|
|
59
|
+
--err "$LOG_DIR/console-forwarder.err")" || exit 1
|
|
60
|
+
printf '%s\n' "$pid" > "$PID_FILE"
|
|
61
|
+
|
|
62
|
+
# A successful launch must never claim capture when the collector died at boot.
|
|
63
|
+
sleep 0.1
|
|
64
|
+
if ! kill -0 "$pid" 2>/dev/null; then
|
|
65
|
+
rm -f "$PID_FILE"
|
|
66
|
+
printf 'start-console-forwarder: collector exited during startup; inspect %s\n' \
|
|
67
|
+
"$LOG_DIR/console-forwarder.err" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
[ "$QUIET" = true ] || printf 'Device console → app-console.log (CDP forwarder pid %s)\n' "$pid" >&2
|
|
@@ -77,45 +77,6 @@ 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
|
-
|
|
119
80
|
write_metro_runner() {
|
|
120
81
|
local runner="$1"
|
|
121
82
|
local clear_flag=""
|
|
@@ -179,7 +140,6 @@ if metro_ready; then
|
|
|
179
140
|
rm -f "$PID_FILE"
|
|
180
141
|
elif metro_listener_valid; then
|
|
181
142
|
printf 'Metro already running and valid on port %s\n' "$PORT" >&2
|
|
182
|
-
start_console_forwarder
|
|
183
143
|
exit 0
|
|
184
144
|
else
|
|
185
145
|
stop_metro_listener || exit 1
|
|
@@ -269,7 +229,6 @@ if [ "$READY" = true ]; then
|
|
|
269
229
|
first_ready_pid="${ready_pids%% *}"
|
|
270
230
|
[ -z "$first_ready_pid" ] || printf '%s\n' "$first_ready_pid" > "$PID_FILE"
|
|
271
231
|
printf 'Metro ready on port %s\n' "$PORT" >&2
|
|
272
|
-
start_console_forwarder
|
|
273
232
|
printf ' Next: mm-harness logs (tail Metro)\n' >&2
|
|
274
233
|
exit 0
|
|
275
234
|
fi
|
|
@@ -261,76 +261,19 @@ 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 agentic bridge\",\"status\":\"fail\"}")
|
|
265
265
|
status="fail"
|
|
266
266
|
else
|
|
267
|
-
checks+=("{\"name\":\"NavigationService
|
|
267
|
+
checks+=("{\"name\":\"NavigationService agentic bridge\",\"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
|
|
271
|
+
checks+=("{\"name\":\"App AgentStepHud\",\"status\":\"fail\"}")
|
|
272
272
|
status="fail"
|
|
273
273
|
else
|
|
274
|
-
checks+=("{\"name\":\"App AgentStepHud
|
|
274
|
+
checks+=("{\"name\":\"App AgentStepHud\",\"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")
|
|
334
277
|
run_with_timeout() {
|
|
335
278
|
local log_path="$1"
|
|
336
279
|
local timeout_s="$2"
|
|
@@ -362,16 +305,14 @@ live_status_ok() {
|
|
|
362
305
|
local device_target="$2"
|
|
363
306
|
cat > "$ARTIFACTS/mobile-status-smoke.recipe.json" <<'JSON'
|
|
364
307
|
{
|
|
365
|
-
"
|
|
308
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
366
309
|
"title": "Mobile runner status smoke",
|
|
367
310
|
"description": "Checks that the installed runner can read Mobile app status before live verification.",
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
"
|
|
372
|
-
|
|
373
|
-
"done": { "action": "end", "status": "pass" }
|
|
374
|
-
}
|
|
311
|
+
"workflow": {
|
|
312
|
+
"entry": "status",
|
|
313
|
+
"nodes": {
|
|
314
|
+
"status": { "action": "app.status", "intent": "Read Mobile app status through the runner-owned bridge", "next": "done" },
|
|
315
|
+
"done": { "action": "end", "status": "pass" }
|
|
375
316
|
}
|
|
376
317
|
}
|
|
377
318
|
}
|
|
@@ -380,15 +321,27 @@ JSON
|
|
|
380
321
|
cd "$TARGET"
|
|
381
322
|
device_args=()
|
|
382
323
|
[ -n "$device_target" ] && device_args=(--device "$device_target")
|
|
383
|
-
"$RUNNER_BIN" run "$ARTIFACTS/mobile-status-smoke.recipe.json" --adapter mobile --
|
|
324
|
+
"$RUNNER_BIN" run "$ARTIFACTS/mobile-status-smoke.recipe.json" --adapter mobile --target "$TARGET" "${device_args[@]}" --artifacts-dir "$ARTIFACTS/runner-status-smoke" --json
|
|
384
325
|
) > "$log_path" 2>&1
|
|
385
326
|
}
|
|
386
327
|
|
|
387
328
|
ensure_live_runtime() {
|
|
388
329
|
local device_target="$1"
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
330
|
+
local attempt
|
|
331
|
+
for attempt in 1 2 3 4 5; do
|
|
332
|
+
if live_status_ok "$ARTIFACTS/logs/app-status-precheck.log" "$device_target"; then
|
|
333
|
+
return 0
|
|
334
|
+
fi
|
|
335
|
+
# A freshly booted simulator can expose its Metro bridge just before
|
|
336
|
+
# `simctl list devices booted` publishes the device. Bound that launch→verify
|
|
337
|
+
# race instead of reporting a false DEVICE_NOT_FOUND immediately.
|
|
338
|
+
if [ "$attempt" -lt 5 ] && grep -q '"code"[[:space:]]*:[[:space:]]*"DEVICE_NOT_FOUND"' "$ARTIFACTS/logs/app-status-precheck.log"; then
|
|
339
|
+
echo "Waiting for Mobile device discovery (${attempt}/5): ${device_target:-configured target}" >&2
|
|
340
|
+
sleep 1
|
|
341
|
+
continue
|
|
342
|
+
fi
|
|
343
|
+
break
|
|
344
|
+
done
|
|
392
345
|
if [ "$AUTO_START" = true ]; then
|
|
393
346
|
cat >&2 <<'EOF'
|
|
394
347
|
Mobile auto-start is not allowed from product-local scripts. Start or prepare the app through the runner/slot runtime, then rerun verify with --no-auto-start.
|
|
@@ -434,17 +387,15 @@ if [ "$STATIC_ONLY" = false ]; then
|
|
|
434
387
|
|
|
435
388
|
cat > "$ARTIFACTS/mobile-v1-live-smoke.recipe.json" <<'JSON'
|
|
436
389
|
{
|
|
437
|
-
"
|
|
390
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
438
391
|
"title": "Mobile v1 runner live bridge smoke",
|
|
439
392
|
"description": "Verifies the installed MetaMask runner can read the React Native debug bridge without mutating wallet state.",
|
|
440
|
-
"
|
|
441
|
-
"
|
|
442
|
-
|
|
443
|
-
"
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
"done": { "action": "end", "status": "pass" }
|
|
447
|
-
}
|
|
393
|
+
"workflow": {
|
|
394
|
+
"entry": "status",
|
|
395
|
+
"nodes": {
|
|
396
|
+
"status": { "action": "app.status", "intent": "Read Mobile app status through the v1 runner", "next": "cdp-probe" },
|
|
397
|
+
"cdp-probe": { "action": "cdp.target", "intent": "Verify the React Native debug bridge target is reachable", "require_reachable": true, "timeout_ms": 15000, "next": "done" },
|
|
398
|
+
"done": { "action": "end", "status": "pass" }
|
|
448
399
|
}
|
|
449
400
|
}
|
|
450
401
|
}
|
|
@@ -460,7 +411,7 @@ JSON
|
|
|
460
411
|
IOS_SIMULATOR="${IOS_SIMULATOR:-$ios_simulator_resolved}" \
|
|
461
412
|
ADB_SERIAL="${ADB_SERIAL:-$adb_serial_resolved}" \
|
|
462
413
|
ANDROID_SERIAL="${ANDROID_SERIAL:-$adb_serial_resolved}" \
|
|
463
|
-
"$RUNNER_BIN" run "$ARTIFACTS/mobile-v1-live-smoke.recipe.json" --adapter mobile --
|
|
414
|
+
"$RUNNER_BIN" run "$ARTIFACTS/mobile-v1-live-smoke.recipe.json" --adapter mobile --target "$TARGET" "${device_args[@]}" --artifacts-dir "$ARTIFACTS/runner-live-smoke" --json
|
|
464
415
|
) > "$ARTIFACTS/logs/runner-live-smoke.log" 2>&1; then
|
|
465
416
|
checks+=("{\"name\":\"runner v1 live bridge smoke\",\"status\":\"pass\"}")
|
|
466
417
|
else
|
|
@@ -24,10 +24,10 @@ REQUIRE_PLATFORM=""
|
|
|
24
24
|
|
|
25
25
|
while [ "$#" -gt 0 ]; do
|
|
26
26
|
case "$1" in
|
|
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 ;;
|
|
27
|
+
--target) [ "$#" -ge 2 ] || { echo "Missing value for --target" >&2; exit 2; }; TARGET="$2"; shift 2 ;;
|
|
28
|
+
--port) [ "$#" -ge 2 ] || { echo "Missing value for --port" >&2; exit 2; }; PORT="$2"; shift 2 ;;
|
|
29
|
+
--max-polls) [ "$#" -ge 2 ] || { echo "Missing value for --max-polls" >&2; exit 2; }; MAX_POLLS="$2"; shift 2 ;;
|
|
30
|
+
--platform) [ "$#" -ge 2 ] || { echo "Missing value for --platform" >&2; exit 2; }; 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,7 +36,12 @@ while [ "$#" -gt 0 ]; do
|
|
|
36
36
|
esac
|
|
37
37
|
done
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
case "$MAX_POLLS" in
|
|
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)"
|
|
40
45
|
|
|
41
46
|
# The matcher reads the required platform from the environment so the value survives
|
|
42
47
|
# into the node subprocess that runs the shared matcher against the status output.
|
|
@@ -128,15 +133,24 @@ try {
|
|
|
128
133
|
' "$MATCH_LIB" "$STATUS_LOG" 2>/dev/null || printf 'none'
|
|
129
134
|
}
|
|
130
135
|
|
|
136
|
+
READY_STREAK=0
|
|
131
137
|
for ATTEMPT in $(seq 1 "$MAX_POLLS"); do
|
|
132
138
|
if bridge_has_route; then
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
READY_STREAK=$((READY_STREAK + 1))
|
|
140
|
+
# The bridge can appear briefly while React Native is still completing its
|
|
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
|
|
135
149
|
fi
|
|
136
150
|
if [ "$ATTEMPT" = "1" ] || [ $((ATTEMPT % 5)) -eq 0 ]; then
|
|
137
151
|
printf 'Waiting for Mobile bridge (%s/%s): %s\n' "$ATTEMPT" "$MAX_POLLS" "$(bridge_wait_reason)" >&2
|
|
138
152
|
fi
|
|
139
|
-
sleep 2
|
|
153
|
+
[ "$ATTEMPT" -ge "$MAX_POLLS" ] || sleep 2
|
|
140
154
|
done
|
|
141
155
|
|
|
142
156
|
cat "$STATUS_LOG" >&2 || true
|
|
@@ -12,27 +12,29 @@
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import crypto from 'node:crypto';
|
|
15
|
+
import { pathToFileURL } from 'node:url';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
export function harnessSourceFingerprint(runnerDir, adapter = 'extension') {
|
|
18
|
+
if (adapter !== 'extension' && adapter !== 'mobile') {
|
|
19
|
+
throw new Error(`Unsupported harness fingerprint adapter: ${adapter}`);
|
|
20
|
+
}
|
|
21
|
+
const sourceDirs = [`adapters/${adapter}`, 'adapters/shared', 'library'];
|
|
21
22
|
const entries = [];
|
|
22
|
-
for (const rel of
|
|
23
|
+
for (const rel of sourceDirs) {
|
|
23
24
|
collect(path.join(runnerDir, rel), runnerDir, entries);
|
|
24
25
|
}
|
|
25
26
|
entries.sort();
|
|
26
27
|
return crypto.createHash('sha256').update(entries.join('\n')).digest('hex');
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
31
|
+
process.stdout.write(`${harnessSourceFingerprint(path.resolve(process.argv[2] ?? '.'), process.argv[3])}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
function collect(dir, root, out) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} catch {
|
|
34
|
-
return; // absent tree contributes nothing
|
|
35
|
-
}
|
|
35
|
+
// These trees are required runner inputs. Fail closed instead of producing a
|
|
36
|
+
// weaker fingerprint that could make a stale injected harness appear current.
|
|
37
|
+
const dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
36
38
|
for (const dirent of dirents) {
|
|
37
39
|
// Skip hidden noise (.DS_Store, editor swap files) so the fingerprint tracks
|
|
38
40
|
// only real source and does not thrash re-injects.
|
|
@@ -172,10 +172,10 @@ function extensionRuntimeContextPath(repo) {
|
|
|
172
172
|
const repoRoot = path.resolve(repo);
|
|
173
173
|
const envCtx = process.env.RECIPE_RUNTIME_CONTEXT;
|
|
174
174
|
if (envCtx) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
// Farmslot may manage runtime context outside the checkout. Treat the
|
|
176
|
+
// explicit environment override as authoritative; containment is not a
|
|
177
|
+
// security boundary and silently dropping it selects the wrong port.
|
|
178
|
+
return path.resolve(envCtx);
|
|
179
179
|
}
|
|
180
180
|
return path.join(repoRoot, recipeRuntimeDir(), 'agentic-runtime.json');
|
|
181
181
|
}
|
|
@@ -198,12 +198,22 @@ export function resolveExtensionRuntimeContext(repo) {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
//
|
|
202
|
-
|
|
201
|
+
// Context and pool describe checkout identity and therefore replace stale
|
|
202
|
+
// inherited env values. The suffix formula is only a fallback and is applied
|
|
203
|
+
// separately by callers so an explicitly supplied env port can still win.
|
|
204
|
+
export function formatExtensionIdentityPorts(repo) {
|
|
203
205
|
const realRepo = path.resolve(repo);
|
|
204
206
|
return mergeKvLayers(
|
|
205
207
|
{ source: resolveExtensionRuntimeContext(realRepo), overwrite: true },
|
|
206
208
|
{ source: resolveSlotPortsByRepo(realRepo), overwrite: false },
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Checkout-scoped extension ports: runtime context (authoritative) → pool → suffix formula.
|
|
213
|
+
export function formatExtensionSlotPorts(repo) {
|
|
214
|
+
const realRepo = path.resolve(repo);
|
|
215
|
+
return mergeKvLayers(
|
|
216
|
+
{ source: formatExtensionIdentityPorts(realRepo), overwrite: true },
|
|
207
217
|
{ source: resolveDefaultExtensionPorts(realRepo), overwrite: false },
|
|
208
218
|
);
|
|
209
219
|
}
|
|
@@ -285,6 +295,7 @@ export const cliFns = {
|
|
|
285
295
|
// Back-compat cli key for one release.
|
|
286
296
|
resolve_farmslot_ports_by_repo: resolveSlotPortsByRepo,
|
|
287
297
|
resolve_default_extension_ports: resolveDefaultExtensionPorts,
|
|
298
|
+
resolve_extension_identity_ports: (repo) => formatExtensionIdentityPorts(repo) || null,
|
|
288
299
|
resolve_extension_runtime_context: resolveExtensionRuntimeContext,
|
|
289
300
|
resolve_extension_slot_ports: (repo) => formatExtensionSlotPorts(repo) || null,
|
|
290
301
|
resolve_extension_runtime_ports: (repo) => resolveExtensionRuntimePorts(repo) || null,
|
|
@@ -52,12 +52,13 @@ apply_extension_slot_ports() {
|
|
|
52
52
|
local repo="${1:-.}" line key val resolved="" ctx_file="" runtime_rel="${RECIPE_RUNTIME_DIR:-temp/recipe/runtime}"
|
|
53
53
|
repo="$(cd "$repo" && pwd)"
|
|
54
54
|
ctx_file="$repo/$runtime_rel/agentic-runtime.json"
|
|
55
|
-
if [ -f "$ctx_file" ]; then
|
|
55
|
+
if [ -z "${RECIPE_RUNTIME_CONTEXT:-}" ] && [ -f "$ctx_file" ]; then
|
|
56
56
|
RECIPE_RUNTIME_CONTEXT="$ctx_file"
|
|
57
|
-
else
|
|
58
|
-
unset RECIPE_RUNTIME_CONTEXT
|
|
59
57
|
fi
|
|
60
|
-
|
|
58
|
+
export RECIPE_RUNTIME_CONTEXT
|
|
59
|
+
# Context/pool are authoritative checkout identity. Formula ports are only
|
|
60
|
+
# fallback values and must not replace explicitly supplied env ports.
|
|
61
|
+
resolved="$(_resolve_ports_cli resolve_extension_identity_ports "$repo" 2>/dev/null)" || resolved=""
|
|
61
62
|
while IFS= read -r line; do
|
|
62
63
|
[ -n "$line" ] || continue
|
|
63
64
|
key="${line%%=*}"
|
|
@@ -68,6 +69,17 @@ apply_extension_slot_ports() {
|
|
|
68
69
|
SLOT_ID) RECIPE_SLOT_ID="$val" ;;
|
|
69
70
|
esac
|
|
70
71
|
done <<< "$resolved"
|
|
72
|
+
resolved="$(_resolve_ports_cli resolve_default_extension_ports "$repo" 2>/dev/null)" || return 0
|
|
73
|
+
while IFS= read -r line; do
|
|
74
|
+
[ -n "$line" ] || continue
|
|
75
|
+
key="${line%%=*}"
|
|
76
|
+
val="${line#*=}"
|
|
77
|
+
case "$key" in
|
|
78
|
+
CDP_PORT) [ -n "${CDP_PORT:-}" ] || CDP_PORT="$val" ;;
|
|
79
|
+
WATCHER_PORT) [ -n "${WATCHER_PORT:-}" ] || WATCHER_PORT="$val" ;;
|
|
80
|
+
SLOT_ID) [ -n "${RECIPE_SLOT_ID:-}" ] || RECIPE_SLOT_ID="$val" ;;
|
|
81
|
+
esac
|
|
82
|
+
done <<< "$resolved"
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
apply_resolved_extension_ports() {
|
|
@@ -111,4 +123,4 @@ apply_resolved_mobile_ports() {
|
|
|
111
123
|
;;
|
|
112
124
|
esac
|
|
113
125
|
done <<< "$resolved"
|
|
114
|
-
}
|
|
126
|
+
}
|