@deeeed/metamask-harness 0.6.2 → 0.7.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/adapters/extension/inject.mjs +7 -0
  3. package/adapters/extension/reattach.sh +210 -0
  4. package/adapters/extension/start-watch.sh +11 -17
  5. package/adapters/manifest.json +65 -9
  6. package/adapters/mobile/lib/tmux-viewer.sh +31 -10
  7. package/adapters/mobile/open-device.sh +14 -1
  8. package/adapters/mobile/stop-metro.sh +1 -1
  9. package/adapters/mobile/yarn-setup.sh +15 -1
  10. package/adapters/shared/activate-repo-ruby.sh +124 -0
  11. package/adapters/shared/open-log-window.sh +55 -0
  12. package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -205
  13. package/adapters/shared/resolve-farmslot-ports.mjs +4 -19
  14. package/adapters/shared/resolve-farmslot-ports.sh +6 -104
  15. package/adapters/shared/resolve-slot-ports-core.mjs +213 -0
  16. package/adapters/shared/resolve-slot-ports.mjs +20 -0
  17. package/adapters/shared/resolve-slot-ports.sh +110 -0
  18. package/adapters/shared/tmux-session.sh +35 -0
  19. package/dist/adapters/extension/runtime-decision.js +4 -0
  20. package/dist/adapters/mobile/provision.js +25 -2
  21. package/dist/adapters/{resolve-farmslot-ports.js → resolve-slot-ports.js} +4 -2
  22. package/dist/adapters/slot-ports.js +8 -10
  23. package/dist/cli-commands.js +1 -1
  24. package/dist/commands/call.js +5 -0
  25. package/dist/commands/doctor.js +21 -1
  26. package/dist/commands/fixtures.js +5 -3
  27. package/dist/commands/launch/extension.js +80 -6
  28. package/dist/commands/launch/index.js +10 -1
  29. package/dist/commands/list-executables.js +48 -0
  30. package/dist/commands/logs.js +25 -2
  31. package/dist/commands/manifest.js +27 -7
  32. package/dist/commands/parse-args.js +1 -0
  33. package/dist/commands/run.js +3 -4
  34. package/dist/live-adapter-contract.js +30 -9
  35. package/dist/mm-harness-cli.js +3 -1
  36. package/dist/paths.js +4 -1
  37. package/package.json +1 -1
@@ -0,0 +1,124 @@
1
+ # activate-repo-ruby.sh — pin the target repo's declared Ruby for non-interactive spawns
2
+ #
3
+ # iOS pod/gem work (yarn setup, native build) reads Ruby from PATH. Under tmux /
4
+ # nohup / a cold checkout the PATH is the system Ruby, which does not match the
5
+ # repo's .ruby-version and fails `bundle install` / `pod install` with native gem
6
+ # build errors. Sourced by the leaves that trigger pods so they run the pinned
7
+ # Ruby, mirroring activate-repo-node.sh for Node.
8
+ #
9
+ # On success sets REPO_RUBY_VERSION / REPO_RUBY_BIN / REPO_RUBY_BIN_DIR /
10
+ # REPO_RUBY_PATH_PREFIX and prepends the bin dir to PATH.
11
+ #
12
+ # Usage:
13
+ # . /path/to/activate-repo-ruby.sh
14
+ # activate_repo_ruby [target-dir] # 0 pinned; 1 none declared; 2 declared but unresolved
15
+ # activate_repo_ruby_best_effort [dir] # never fails the caller; warns + teaches on 2
16
+
17
+ _repo_ruby_matches_req() {
18
+ local req="$1" actual="${2:-}"
19
+ [ -n "$actual" ] || actual="$("$REPO_RUBY_BIN" -e 'print RUBY_VERSION' 2>/dev/null || true)"
20
+ [ -n "$actual" ] || return 1
21
+ [ "$actual" = "$req" ] && return 0
22
+ case "$req" in
23
+ *.*.*) return 1 ;;
24
+ *.*) [[ "$actual" == "$req."* ]] ;;
25
+ *) [[ "$actual" == "$req"* ]] ;;
26
+ esac
27
+ }
28
+
29
+ _repo_ruby_set_from_bin() {
30
+ local candidate="$1"
31
+ [ -n "$candidate" ] && [ -x "$candidate" ] || return 1
32
+ REPO_RUBY_BIN="$candidate"
33
+ REPO_RUBY_BIN_DIR="$(cd "$(dirname "$REPO_RUBY_BIN")" && pwd)"
34
+ REPO_RUBY_PATH_PREFIX="${REPO_RUBY_BIN_DIR}:"
35
+ export PATH="${REPO_RUBY_BIN_DIR}:${PATH}"
36
+ return 0
37
+ }
38
+
39
+ _repo_ruby_try_req() {
40
+ local target="$1" req="$2"
41
+ [ -n "$req" ] || return 1
42
+ REPO_RUBY_VERSION="$req"
43
+
44
+ if command -v asdf >/dev/null 2>&1; then
45
+ local asdf_root
46
+ asdf_root="$(asdf where ruby "$req" 2>/dev/null || true)"
47
+ if [ -n "$asdf_root" ] && _repo_ruby_set_from_bin "$asdf_root/bin/ruby" && _repo_ruby_matches_req "$req"; then
48
+ echo "[env] ruby $("$REPO_RUBY_BIN" -e 'print RUBY_VERSION') from .ruby-version/.tool-versions via asdf ($req)" >&2
49
+ return 0
50
+ fi
51
+ fi
52
+
53
+ if command -v rbenv >/dev/null 2>&1; then
54
+ local rbenv_root
55
+ rbenv_root="$(rbenv root 2>/dev/null || true)"
56
+ if [ -n "$rbenv_root" ] && _repo_ruby_set_from_bin "$rbenv_root/versions/$req/bin/ruby" && _repo_ruby_matches_req "$req"; then
57
+ echo "[env] ruby $("$REPO_RUBY_BIN" -e 'print RUBY_VERSION') from .ruby-version via rbenv ($req)" >&2
58
+ return 0
59
+ fi
60
+ fi
61
+
62
+ if command -v mise >/dev/null 2>&1; then
63
+ local mise_bin
64
+ mise_bin="$(mise x -C "$target" -- which ruby 2>/dev/null || true)"
65
+ if _repo_ruby_set_from_bin "$mise_bin" && _repo_ruby_matches_req "$req"; then
66
+ echo "[env] ruby $("$REPO_RUBY_BIN" -e 'print RUBY_VERSION') from .ruby-version via mise ($req)" >&2
67
+ return 0
68
+ fi
69
+ fi
70
+ return 1
71
+ }
72
+
73
+ activate_repo_ruby() {
74
+ local target="${1:-.}"
75
+ REPO_RUBY_VERSION=""
76
+ REPO_RUBY_BIN=""
77
+ REPO_RUBY_BIN_DIR=""
78
+ REPO_RUBY_PATH_PREFIX=""
79
+
80
+ local declared=false req=""
81
+ if [ -f "$target/.ruby-version" ]; then
82
+ declared=true
83
+ req="$(tr -d ' \t\r\n' < "$target/.ruby-version")"
84
+ req="${req#ruby-}"
85
+ _repo_ruby_try_req "$target" "$req" && return 0
86
+ fi
87
+ if [ -z "$req" ] && [ -f "$target/.tool-versions" ]; then
88
+ declared=true
89
+ req="$(awk '/^ruby / {print $2; exit}' "$target/.tool-versions")"
90
+ _repo_ruby_try_req "$target" "$req" && return 0
91
+ fi
92
+
93
+ if [ "$declared" != true ]; then
94
+ return 1
95
+ fi
96
+
97
+ # Declared already-on-PATH match (e.g. chruby/system already correct).
98
+ local path_ruby
99
+ path_ruby="$(command -v ruby 2>/dev/null || true)"
100
+ if [ -n "$req" ] && _repo_ruby_set_from_bin "$path_ruby" && _repo_ruby_matches_req "$req"; then
101
+ echo "[env] ruby $("$REPO_RUBY_BIN" -e 'print RUBY_VERSION') already on PATH ($req)" >&2
102
+ return 0
103
+ fi
104
+
105
+ REPO_RUBY_BIN=""
106
+ REPO_RUBY_BIN_DIR=""
107
+ REPO_RUBY_PATH_PREFIX=""
108
+ return 2
109
+ }
110
+
111
+ # Best-effort: never fails the caller. On a declared-but-unresolved Ruby, warn with
112
+ # a teaching hint so a subsequent pod/gem failure is diagnosable (system Ruby).
113
+ activate_repo_ruby_best_effort() {
114
+ local target="${1:-.}"
115
+ # Guard the call so a non-zero return (no version / unresolved) never trips the
116
+ # caller's set -e before we can inspect the code.
117
+ local rc=0
118
+ activate_repo_ruby "$target" || rc=$?
119
+ if [ "$rc" -eq 2 ]; then
120
+ echo "[env] repo pins ruby ${REPO_RUBY_VERSION:-?} (.ruby-version/.tool-versions) but no installed manager provided it; using system ruby." >&2
121
+ echo " Next: install it — asdf install ruby ${REPO_RUBY_VERSION:-<version>} (or rbenv install) — then re-run, so iOS gems build against the pinned Ruby" >&2
122
+ fi
123
+ return 0
124
+ }
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # open-log-window.sh — (re)open a read-only tmux tail window for a dev-server log
3
+ # in the run-owned session, WITHOUT touching the dev-server process. Idempotent:
4
+ # an existing same-name window is replaced with a fresh tail. Used by
5
+ # `mm-harness logs --window` to recover a window that was closed while Metro/webpack
6
+ # keeps running.
7
+ #
8
+ # Args: --window <name> --log <path> [--runtime-dir <dir>]
9
+ # Exit: 0 opened; 1 no run-owned session / no tmux / log missing (teaches); 2 bad args.
10
+ set -euo pipefail
11
+
12
+ WINDOW=""
13
+ LOG=""
14
+ RUNTIME_DIR=""
15
+ require_value() { [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; }
16
+ while [ "$#" -gt 0 ]; do
17
+ case "$1" in
18
+ --window) require_value "$@"; WINDOW="$2"; shift 2 ;;
19
+ --log) require_value "$@"; LOG="$2"; shift 2 ;;
20
+ --runtime-dir) require_value "$@"; RUNTIME_DIR="$2"; shift 2 ;;
21
+ -h|--help) echo "Usage: open-log-window.sh --window <name> --log <path> [--runtime-dir <dir>]"; exit 0 ;;
22
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
23
+ esac
24
+ done
25
+ [ -n "$WINDOW" ] && [ -n "$LOG" ] || { echo "open-log-window: --window and --log are required" >&2; exit 2; }
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ # shellcheck disable=SC1091
29
+ for _src in "$SCRIPT_DIR/tmux-session.sh" "$SCRIPT_DIR/lib/tmux-session.sh"; do
30
+ [ -f "$_src" ] && { . "$_src"; break; }
31
+ done
32
+ unset _src
33
+
34
+ if ! command -v tmux >/dev/null 2>&1; then
35
+ echo "open-log-window: tmux is not installed; cannot open a log window." >&2
36
+ echo " Next: tail the log directly — mm-harness logs --full" >&2
37
+ exit 1
38
+ fi
39
+ if [ ! -f "$LOG" ]; then
40
+ echo "open-log-window: no log at $LOG (dev server not started for this checkout)." >&2
41
+ echo " Next: start it — mm-harness launch" >&2
42
+ exit 1
43
+ fi
44
+ session=""
45
+ command -v resolve_run_tmux_session >/dev/null 2>&1 && session="$(resolve_run_tmux_session "$RUNTIME_DIR")"
46
+ if [ -z "$session" ] || ! tmux has-session -t "=$session" 2>/dev/null; then
47
+ echo "open-log-window: no run-owned tmux session to place the window in." >&2
48
+ echo " Next: run inside your slot's tmux session, or set RECIPE_TMUX_SESSION=<session>, then retry" >&2
49
+ exit 1
50
+ fi
51
+
52
+ tmux kill-window -t "${session}:${WINDOW}" 2>/dev/null || true
53
+ log_q="$(printf '%q' "$LOG")"
54
+ tmux new-window -d -t "$session" -n "$WINDOW" "exec tail -n +1 -F $log_q"
55
+ echo "log window → tmux ${session}:${WINDOW}" >&2
@@ -1,205 +1,3 @@
1
- // Farmslot pool + slot-suffix port resolution pure JS for runner and overlay leaves.
2
-
3
- import fs from 'node:fs';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- const coreDir = path.dirname(fileURLToPath(import.meta.url));
9
-
10
- function pathDefault(key) {
11
- for (const candidate of [
12
- path.join(coreDir, 'path-defaults.json'),
13
- path.join(coreDir, '../../adapters/shared/path-defaults.json'),
14
- ]) {
15
- if (!fs.existsSync(candidate)) continue;
16
- const value = JSON.parse(fs.readFileSync(candidate, 'utf8'))[key];
17
- if (value) return value;
18
- }
19
- throw new Error(`Missing path default: ${key}`);
20
- }
21
-
22
- function recipeRuntimeDir() {
23
- return process.env.RECIPE_RUNTIME_DIR || pathDefault('recipeRuntimeDir');
24
- }
25
-
26
- export function formatKvLines(kv) {
27
- const lines = [];
28
- if (kv.CDP_PORT !== undefined) lines.push(`CDP_PORT=${kv.CDP_PORT}`);
29
- if (kv.WATCHER_PORT !== undefined) lines.push(`WATCHER_PORT=${kv.WATCHER_PORT}`);
30
- if (kv.SLOT_ID) lines.push(`SLOT_ID=${kv.SLOT_ID}`);
31
- if (kv.IOS_SIMULATOR) lines.push(`IOS_SIMULATOR=${kv.IOS_SIMULATOR}`);
32
- return lines.length ? `${lines.join('\n')}\n` : '';
33
- }
34
-
35
- export function realRepoPath(repo) {
36
- if (!repo) return null;
37
- try {
38
- return fs.realpathSync(repo);
39
- } catch {
40
- return null;
41
- }
42
- }
43
-
44
- export function inferSlotSuffix(repo) {
45
- const base = path.basename(repo);
46
- const m = /-(\d+)$/u.exec(base);
47
- return m ? m[1] : null;
48
- }
49
-
50
- function scoreSlot(slot, slotSuffix) {
51
- const resources = slot.resources ?? {};
52
- const cdp = resources.browser?.cdp_port;
53
- const port = resources['dev-server']?.port;
54
- const simulator = resources['ios-sim']?.simulator;
55
- const session = slot.session ?? '';
56
- const slotId = slot.id ?? '';
57
- let score = 0;
58
- if (cdp !== undefined) score += 100;
59
- if (port !== undefined) score += 10;
60
- if (slotSuffix) {
61
- if (session === `mme-${slotSuffix}`) score += 50;
62
- if (slotId.endsWith(`mme-${slotSuffix}`) || slotId.includes(`-mme-${slotSuffix}`)) score += 40;
63
- }
64
- if (!slotId.toLowerCase().includes('demo')) score += 5;
65
- return [score, cdp, port, slotId, simulator];
66
- }
67
-
68
- export function resolveFarmslotPortsByRepo(repo) {
69
- const realRepo = realRepoPath(repo);
70
- if (!realRepo) return null;
71
-
72
- const slotSuffix = inferSlotSuffix(realRepo);
73
- const roots = [];
74
- // A slot checkout lives at <workspace>/repos/<slot>; that workspace's own
75
- // pool is the authoritative one for this repo — machine-global fallbacks
76
- // (env root, dev checkout) apply only when the repo is not workspace-shaped.
77
- if (path.basename(path.dirname(realRepo)) === 'repos') {
78
- roots.push(path.join(path.dirname(path.dirname(realRepo)), 'farmslot'));
79
- }
80
- if (process.env.FARMSLOT_ROOT) roots.push(process.env.FARMSLOT_ROOT);
81
- roots.push(path.join(os.homedir(), 'dev', 'farmslot'));
82
-
83
- for (const root of roots) {
84
- if (!root || !fs.existsSync(path.join(root, 'pool'))) continue;
85
- const poolDir = path.join(root, 'pool');
86
- let best = null;
87
-
88
- for (const name of fs.readdirSync(poolDir).sort()) {
89
- if (!name.endsWith('.json') || name.includes('.bak.')) continue;
90
- let data;
91
- try {
92
- data = JSON.parse(fs.readFileSync(path.join(poolDir, name), 'utf8'));
93
- } catch {
94
- continue;
95
- }
96
- for (const slot of data.slots ?? []) {
97
- const slotRepo = slot.repo ?? '';
98
- if (!slotRepo) continue;
99
- let slotReal;
100
- try {
101
- slotReal = fs.realpathSync(slotRepo);
102
- } catch {
103
- continue;
104
- }
105
- if (slotReal !== realRepo) continue;
106
- const scored = scoreSlot(slot, slotSuffix);
107
- if (!best || scored[0] > best[0]) best = scored;
108
- }
109
- }
110
-
111
- if (best) {
112
- const [, cdp, port, slotId, simulator] = best;
113
- return formatKvLines({
114
- ...(cdp !== undefined ? { CDP_PORT: cdp } : {}),
115
- ...(port !== undefined ? { WATCHER_PORT: port } : {}),
116
- ...(slotId ? { SLOT_ID: slotId } : {}),
117
- ...(simulator ? { IOS_SIMULATOR: simulator } : {}),
118
- });
119
- }
120
- }
121
- return null;
122
- }
123
-
124
- export function resolveDefaultExtensionPorts(repo) {
125
- const n = inferSlotSuffix(repo);
126
- if (!n) return null;
127
- return formatKvLines({
128
- CDP_PORT: 6660 + Number(n),
129
- WATCHER_PORT: 9010 + Number(n),
130
- SLOT_ID: `local-extension-${n}`,
131
- });
132
- }
133
-
134
- export function resolveExtensionRuntimePorts(repo) {
135
- return resolveFarmslotPortsByRepo(repo) ?? resolveDefaultExtensionPorts(repo) ?? '';
136
- }
137
-
138
- export function resolveMobileSlotDefaults(repo) {
139
- const n = inferSlotSuffix(repo);
140
- if (!n) return null;
141
- return formatKvLines({
142
- WATCHER_PORT: 8060 + Number(n),
143
- IOS_SIMULATOR: `mm-${n}`,
144
- SLOT_ID: `local-mobile-${n}`,
145
- });
146
- }
147
-
148
- export function resolveMobileRuntimeContext(repo) {
149
- const ctxPath = path.join(repo, recipeRuntimeDir(), 'agentic-runtime.json');
150
- if (fs.existsSync(ctxPath)) {
151
- try {
152
- const c = JSON.parse(fs.readFileSync(ctxPath, 'utf8'));
153
- if (c.simulator || (c.metroPort != null && c.metroPort !== '')) {
154
- return formatKvLines({
155
- ...(c.metroPort != null && c.metroPort !== '' ? { WATCHER_PORT: c.metroPort } : {}),
156
- ...(c.simulator ? { IOS_SIMULATOR: c.simulator } : {}),
157
- ...(c.slotId ? { SLOT_ID: c.slotId } : {}),
158
- });
159
- }
160
- } catch {
161
- /* unreadable context falls through to the provision baseline */
162
- }
163
- }
164
- return resolveMobileProvisionBaseline(repo);
165
- }
166
-
167
- // A provisioned-but-unprepared slot has no agentic-runtime.json yet, but the
168
- // provision baseline records the same authoritative simulator/port identity —
169
- // without it, a fresh slot's first launch degrades to the simctl `booted`
170
- // alias and misses the installed dev client entirely.
171
- function resolveMobileProvisionBaseline(repo) {
172
- const basePath = path.join(repo, recipeRuntimeDir(), 'runway-provision.json');
173
- if (!fs.existsSync(basePath)) return null;
174
- try {
175
- const c = JSON.parse(fs.readFileSync(basePath, 'utf8'));
176
- const simulator = c.simulator?.name || c.simulator?.udid;
177
- const watcherPort = c.watcherPort != null && c.watcherPort !== '' ? c.watcherPort : undefined;
178
- if (!simulator && watcherPort === undefined) return null;
179
- return formatKvLines({
180
- ...(watcherPort !== undefined ? { WATCHER_PORT: watcherPort } : {}),
181
- ...(simulator ? { IOS_SIMULATOR: simulator } : {}),
182
- ...(c.slotId ? { SLOT_ID: c.slotId } : {}),
183
- });
184
- } catch {
185
- return null;
186
- }
187
- }
188
-
189
- export function resolveMobileRuntimePorts(repo) {
190
- return (
191
- resolveMobileRuntimeContext(repo)
192
- ?? resolveFarmslotPortsByRepo(repo)
193
- ?? resolveMobileSlotDefaults(repo)
194
- ?? ''
195
- );
196
- }
197
-
198
- export const cliFns = {
199
- resolve_farmslot_ports_by_repo: resolveFarmslotPortsByRepo,
200
- resolve_default_extension_ports: resolveDefaultExtensionPorts,
201
- resolve_extension_runtime_ports: (repo) => resolveExtensionRuntimePorts(repo) || null,
202
- resolve_mobile_runtime_context: resolveMobileRuntimeContext,
203
- resolve_mobile_slot_defaults: resolveMobileSlotDefaults,
204
- resolve_mobile_runtime_ports: (repo) => resolveMobileRuntimePorts(repo) || null,
205
- };
1
+ // Back-compat shim: the port-resolution core is now resolve-slot-ports-core.mjs.
2
+ // Re-exports it verbatim for one release so importers of the old path keep resolving.
3
+ export * from './resolve-slot-ports-core.mjs';
@@ -1,20 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // Node leaf for port resolution self-contained for runner and injected overlay copies.
3
-
4
- import { cliFns } from './resolve-farmslot-ports-core.mjs';
5
-
6
- const [, , fn, repo] = process.argv;
7
- if (!fn || fn === '-h' || fn === '--help') {
8
- process.stderr.write('Usage: resolve-farmslot-ports.mjs <fn> <repo>\n');
9
- process.exit(fn ? 0 : 2);
10
- }
11
-
12
- const handler = cliFns[fn];
13
- if (!handler) {
14
- process.stderr.write(`resolve-farmslot-ports: unknown fn: ${fn}\n`);
15
- process.exit(2);
16
- }
17
-
18
- const out = handler(repo ?? '.');
19
- if (!out?.trim()) process.exit(1);
20
- process.stdout.write(out.endsWith('\n') ? out : `${out}\n`);
2
+ // Back-compat shim: this leaf is now resolve-slot-ports.mjs. Delegates to it for one
3
+ // release so `node resolve-farmslot-ports.mjs <fn> <repo>` keeps working (the new CLI
4
+ // reads the same argv positions on import).
5
+ import './resolve-slot-ports.mjs';
@@ -1,105 +1,7 @@
1
1
  #!/usr/bin/env bash
2
- # resolve-farmslot-ports.sh bash-compat wrapper over resolve-farmslot-ports.mjs
3
- #
4
- # Farmslot pool JSON is authoritative when a slot repo matches; otherwise fall back
5
- # to the local-extension-N formula (6660+N / 9010+N).
6
- #
7
- # Usage:
8
- # . resolve-farmslot-ports.sh
9
- # resolve_extension_runtime_ports /path/to/metamask-extension-N
10
- # -> prints CDP_PORT=... WATCHER_PORT=... SLOT_ID=... lines
11
-
12
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
- _RESOLVE_PORTS_MJS="$SCRIPT_DIR/resolve-farmslot-ports.mjs"
14
-
15
- _resolve_ports_cli() {
16
- node "$_RESOLVE_PORTS_MJS" "$1" "${2:-.}"
17
- }
18
-
19
- resolve_farmslot_ports_by_repo() {
20
- _resolve_ports_cli resolve_farmslot_ports_by_repo "$1"
21
- }
22
-
23
- infer_extension_slot_suffix() {
24
- local repo="$1" base
25
- base="$(basename "$repo")"
26
- if [[ "$base" =~ -([0-9]+)$ ]]; then
27
- printf '%s' "${BASH_REMATCH[1]}"
28
- return 0
29
- fi
30
- return 1
31
- }
32
-
33
- resolve_default_extension_ports() {
34
- _resolve_ports_cli resolve_default_extension_ports "$1"
35
- }
36
-
37
- resolve_extension_runtime_ports() {
38
- _resolve_ports_cli resolve_extension_runtime_ports "${1:-.}"
39
- }
40
-
41
- apply_resolved_extension_ports() {
42
- local repo="${1:-.}" line key val from_pool=false resolved=""
43
- if resolved="$(_resolve_ports_cli resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
44
- from_pool=true
45
- elif resolved="$(_resolve_ports_cli resolve_default_extension_ports "$repo" 2>/dev/null)"; then
46
- from_pool=false
47
- else
48
- return 0
49
- fi
50
- while IFS= read -r line; do
51
- [ -n "$line" ] || continue
52
- key="${line%%=*}"
53
- val="${line#*=}"
54
- case "$key" in
55
- CDP_PORT)
56
- if [ "$from_pool" = true ] || [ -z "${CDP_PORT:-}" ]; then CDP_PORT="$val"; fi
57
- ;;
58
- WATCHER_PORT)
59
- if [ "$from_pool" = true ] || [ -z "${WATCHER_PORT:-}" ]; then WATCHER_PORT="$val"; fi
60
- ;;
61
- SLOT_ID)
62
- if [ "$from_pool" = true ] || [ -z "${RECIPE_SLOT_ID:-}" ]; then RECIPE_SLOT_ID="$val"; fi
63
- ;;
64
- esac
65
- done <<< "$resolved"
66
- }
67
-
68
- resolve_mobile_slot_defaults() {
69
- _resolve_ports_cli resolve_mobile_slot_defaults "$1"
70
- }
71
-
72
- resolve_mobile_runtime_context() {
73
- _resolve_ports_cli resolve_mobile_runtime_context "$1"
74
- }
75
-
76
- resolve_mobile_runtime_ports() {
77
- _resolve_ports_cli resolve_mobile_runtime_ports "${1:-.}"
78
- }
79
-
80
- apply_resolved_mobile_ports() {
81
- local repo="${1:-.}" line key val from_pool=false resolved=""
82
- if resolved="$(_resolve_ports_cli resolve_farmslot_ports_by_repo "$repo" 2>/dev/null)"; then
83
- from_pool=true
84
- elif resolved="$(_resolve_ports_cli resolve_mobile_slot_defaults "$repo" 2>/dev/null)"; then
85
- from_pool=false
86
- else
87
- return 0
88
- fi
89
- while IFS= read -r line; do
90
- [ -n "$line" ] || continue
91
- key="${line%%=*}"
92
- val="${line#*=}"
93
- case "$key" in
94
- WATCHER_PORT)
95
- if [ "$from_pool" = true ] || [ -z "${WATCHER_PORT:-}" ]; then WATCHER_PORT="$val"; fi
96
- ;;
97
- IOS_SIMULATOR)
98
- if [ "$from_pool" = true ] || [ -z "${IOS_SIMULATOR:-}" ]; then IOS_SIMULATOR="$val"; fi
99
- ;;
100
- SLOT_ID)
101
- if [ "$from_pool" = true ] || [ -z "${RECIPE_SLOT_ID:-}" ]; then RECIPE_SLOT_ID="$val"; fi
102
- ;;
103
- esac
104
- done <<< "$resolved"
105
- }
2
+ # Back-compat shim: this wrapper is now resolve-slot-ports.sh. Sources it for one
3
+ # release so old callers keep the same sourced function names (including the
4
+ # resolve_farmslot_ports_by_repo alias defined there).
5
+ _SHIM_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ # shellcheck disable=SC1091
7
+ . "$_SHIM_DIR/resolve-slot-ports.sh"