@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.
- package/CHANGELOG.md +77 -0
- package/adapters/extension/inject.mjs +7 -0
- package/adapters/extension/reattach.sh +210 -0
- package/adapters/extension/start-watch.sh +11 -17
- package/adapters/manifest.json +65 -9
- package/adapters/mobile/lib/tmux-viewer.sh +31 -10
- package/adapters/mobile/open-device.sh +14 -1
- package/adapters/mobile/stop-metro.sh +1 -1
- package/adapters/mobile/yarn-setup.sh +15 -1
- package/adapters/shared/activate-repo-ruby.sh +124 -0
- package/adapters/shared/open-log-window.sh +55 -0
- package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -205
- package/adapters/shared/resolve-farmslot-ports.mjs +4 -19
- package/adapters/shared/resolve-farmslot-ports.sh +6 -104
- package/adapters/shared/resolve-slot-ports-core.mjs +213 -0
- package/adapters/shared/resolve-slot-ports.mjs +20 -0
- package/adapters/shared/resolve-slot-ports.sh +110 -0
- package/adapters/shared/tmux-session.sh +35 -0
- package/dist/adapters/extension/runtime-decision.js +4 -0
- package/dist/adapters/mobile/provision.js +25 -2
- package/dist/adapters/{resolve-farmslot-ports.js → resolve-slot-ports.js} +4 -2
- package/dist/adapters/slot-ports.js +8 -10
- package/dist/cli-commands.js +1 -1
- package/dist/commands/call.js +5 -0
- package/dist/commands/doctor.js +21 -1
- package/dist/commands/fixtures.js +5 -3
- package/dist/commands/launch/extension.js +80 -6
- package/dist/commands/launch/index.js +10 -1
- package/dist/commands/list-executables.js +48 -0
- package/dist/commands/logs.js +25 -2
- package/dist/commands/manifest.js +27 -7
- package/dist/commands/parse-args.js +1 -0
- package/dist/commands/run.js +3 -4
- package/dist/live-adapter-contract.js +30 -9
- package/dist/mm-harness-cli.js +3 -1
- package/dist/paths.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
The "version people use": every remaining identified fix/improvement batched on the
|
|
6
|
+
0.6.x stability foundation. Built on top of the extension quick-reuse regression fix
|
|
7
|
+
(0.6.3, below). One themed commit per item; full contract suite green (macOS).
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`call --list` / `run --list`** — one scope-aware listing of everything invocable
|
|
11
|
+
for the detected adapter: the fuzzy short-name action universe (each action's full
|
|
12
|
+
name plus its unambiguous short name — what `call` accepts) and the runnable
|
|
13
|
+
library flows (what `run <flow>` accepts). No `<action>`/`<recipe>` required; same
|
|
14
|
+
shape under `--json`.
|
|
15
|
+
- **`logs --window`** — (re)open the read-only tmux tail window for the resolved
|
|
16
|
+
dev-server port without touching the process (recovers a window closed while
|
|
17
|
+
Metro/webpack keeps running); teaches when there is no run-owned session or log.
|
|
18
|
+
- **`doctor` surfaces Screen-Recording / capture readiness** (macOS) — shells
|
|
19
|
+
`capture-helper doctor` and reports pass/WARN with the grant step; capture is
|
|
20
|
+
optional (absent binary → no row, never a doctor failure). Seam: `CAPTURE_HELPER_PATH`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- **`fixtures set` is never silent** — an upfront intent line the moment work begins,
|
|
24
|
+
plus the mobile setup-wallet leaf's own progress streamed live (heartbeat) during
|
|
25
|
+
the 20-40s wallet build; stdout stays a clean `--json` envelope.
|
|
26
|
+
- **`actions --action <name>` fuzzy-resolves like `call`** (exact full name → exact
|
|
27
|
+
final segment → substring) and teaches the vocabulary on an unknown name (exit 2,
|
|
28
|
+
`--json` error envelope) instead of throwing.
|
|
29
|
+
- **`run` auto-detects the adapter** from the target/cwd when `--adapter` is absent
|
|
30
|
+
(parity with call/doctor); teaches when the repo type cannot be detected.
|
|
31
|
+
- **Log-tail windows follow a run-owned session ladder** — `RECIPE_TMUX_SESSION` →
|
|
32
|
+
the checkout's `agentic-runtime.json` `session` → the current tmux client — with no
|
|
33
|
+
slot-number derivation (the orchestrator names the session; the harness only
|
|
34
|
+
populates windows). Stale `metro-<port>` windows whose port has no live listener
|
|
35
|
+
are swept on Metro start.
|
|
36
|
+
- **Extension slot ports fill independently** — the resolver no longer early-returns
|
|
37
|
+
once CDP is known, so a context that pins only `cdpPort` still gets `WATCHER_PORT`
|
|
38
|
+
from the pool/formula.
|
|
39
|
+
- Orchestrator-neutral protocol-root env: `METAMASK_RUNNER_PROTOCOL_ROOT` is primary,
|
|
40
|
+
`FARMSLOT_ROOT` a back-compat alias for one release.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- **Extension `launch` teaches `yarn install --immutable` on missing/stale deps** —
|
|
44
|
+
a pre-watcher deps-freshness probe fails fast (teaching only, never auto-installs)
|
|
45
|
+
instead of letting webpack crash deep on stale `node_modules`; gated so a reusable
|
|
46
|
+
live runtime (reload-in-place) is never blocked.
|
|
47
|
+
- **Core actions run under the target checkout's own tsx** — a core perps action
|
|
48
|
+
dynamically imports the checkout's controller TypeScript, so it now runs under tsx
|
|
49
|
+
(resolved from the target first — the published harness ships none; a thin-installed
|
|
50
|
+
slot has it) rather than bare node throwing `ERR_MODULE_NOT_FOUND`; teaches
|
|
51
|
+
`yarn install` / `yarn build` when no tsx is found.
|
|
52
|
+
- **Repo-pinned Ruby for iOS pod/gem work** — `yarn setup` / the iOS native build
|
|
53
|
+
activate the checkout's `.ruby-version` (asdf → rbenv → mise → PATH) so a cold
|
|
54
|
+
checkout does not build native gems against the system Ruby; teaches the install
|
|
55
|
+
when the pinned Ruby is unresolved.
|
|
56
|
+
- **iOS deep-link dialog pre-approval at provisioning** — the dev-client scheme
|
|
57
|
+
approval is persisted right after app install, so the first headless `openurl` does
|
|
58
|
+
not pause on the "Open in <App>?" dialog (the launch-time write can race).
|
|
59
|
+
- **Extension quick-tier webpack tail window** is regression-guarded (Metro parity).
|
|
60
|
+
|
|
61
|
+
### Internal
|
|
62
|
+
- Dependabot: no high/critical advisories remain (existing `resolutions` cover them).
|
|
63
|
+
- Boring-library swaps (pidtree, ajv) declined per the "delete hand-rolled code with a
|
|
64
|
+
bug history" bar: the ps/lsof process detection is contract-tested and working, and
|
|
65
|
+
manifest validation is already schema-driven via `@farmslot/protocol`.
|
|
66
|
+
- Orchestrator-neutral port leaf: `resolve-farmslot-ports{,-core}.mjs` / `.sh` and
|
|
67
|
+
`src/adapters/resolve-farmslot-ports.ts` renamed to `resolve-slot-ports*`; the
|
|
68
|
+
function `resolveFarmslotPortsByRepo`, the bash function / cli key
|
|
69
|
+
`resolve_farmslot_ports_by_repo`, and the JS export renamed to the `*slot*`
|
|
70
|
+
equivalents. The old filenames, bash function, cli key, and JS export are kept as
|
|
71
|
+
delegating shims/aliases for ONE RELEASE (deprecation window) — the next release
|
|
72
|
+
removes them. The pool-path constants the leaf reads are left as-is (orchestrator
|
|
73
|
+
data location, not a callable surface).
|
|
74
|
+
|
|
75
|
+
## 0.6.3
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
- **Extension `launch` reuses a healthy live runtime instead of always clean-building** — quick `launch` / `launch --sidepanel` on an extension slot now probes for a reusable runtime (webpack watcher LISTENing on `WATCHER_PORT` + CDP `/json/version` reachable on `CDP_PORT` + dist not stale). When all hold it REATTACHES: re-snapshots `dist/chrome` into the loaded runtime-dist and reloads the extension in place over CDP (service worker + pages), re-asserts the side panel, and applies `--url` — against the existing slot Chrome profile, with no watcher stop, webpack cache clear, or Chrome kill/relaunch. Reuse is best-effort: a failed probe (watcher down, CDP unreachable, stale dist) falls through to the clean path, and `--build` is the explicit escape that always clean-builds + relaunches a fresh runtime (no probe) to force-recover a broken slot.
|
|
79
|
+
|
|
3
80
|
## 0.6.2 - 2026-07-06
|
|
4
81
|
|
|
5
82
|
### Added
|
|
@@ -101,6 +101,7 @@ copyFile(path.join(runnerDir, 'adapters/extension/snapshot-dist.sh'), path.join(
|
|
|
101
101
|
copyFile(path.join(runnerDir, 'adapters/extension/pin-remote-flags.cjs'), path.join(harnessDir, 'scripts/pin-remote-flags.cjs'));
|
|
102
102
|
copyFile(path.join(runnerDir, 'adapters/extension/seed-fixture.sh'), path.join(harnessDir, 'scripts/seed-fixture.sh'));
|
|
103
103
|
copyFile(path.join(runnerDir, 'adapters/extension/sidepanel-toggle.sh'), path.join(harnessDir, 'scripts/sidepanel-toggle.sh'));
|
|
104
|
+
copyFile(path.join(runnerDir, 'adapters/extension/reattach.sh'), path.join(harnessDir, 'scripts/reattach.sh'));
|
|
104
105
|
copyFile(path.join(runnerDir, 'adapters/extension/readiness.mjs'), path.join(harnessDir, 'scripts/readiness.mjs'));
|
|
105
106
|
copyFile(path.join(runnerDir, 'adapters/extension/wallet-fixture-state.cjs'), path.join(harnessDir, 'scripts/wallet-fixture-state.cjs'));
|
|
106
107
|
copyFile(path.join(runnerDir, 'adapters/extension/verify.sh'), path.join(harnessDir, 'scripts/verify.sh'));
|
|
@@ -108,11 +109,16 @@ copyFile(path.join(runnerDir, 'adapters/extension/refresh-build.sh'), path.join(
|
|
|
108
109
|
copyFile(path.join(runnerDir, 'adapters/extension/ensure-browser.sh'), path.join(harnessDir, 'scripts/reopen-browser.sh'));
|
|
109
110
|
copyFile(path.join(runnerDir, 'adapters/shared/harness-path.sh'), path.join(harnessDir, 'scripts/lib/harness-path.sh'));
|
|
110
111
|
copyFile(path.join(runnerDir, 'adapters/shared/activate-repo-node.sh'), path.join(harnessDir, 'scripts/lib/activate-repo-node.sh'));
|
|
112
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports.sh'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports.sh'));
|
|
113
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports.mjs'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports.mjs'));
|
|
114
|
+
copyFile(path.join(runnerDir, 'adapters/shared/resolve-slot-ports-core.mjs'), path.join(harnessDir, 'scripts/lib/resolve-slot-ports-core.mjs'));
|
|
115
|
+
// Back-compat shims (one release) so an overlay consumer using the old leaf names still resolves.
|
|
111
116
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports.sh'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports.sh'));
|
|
112
117
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports.mjs'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports.mjs'));
|
|
113
118
|
copyFile(path.join(runnerDir, 'adapters/shared/resolve-farmslot-ports-core.mjs'), path.join(harnessDir, 'scripts/lib/resolve-farmslot-ports-core.mjs'));
|
|
114
119
|
copyFile(path.join(runnerDir, 'adapters/shared/path-defaults.json'), path.join(harnessDir, 'scripts/lib/path-defaults.json'));
|
|
115
120
|
copyFile(path.join(runnerDir, 'adapters/shared/json-field.sh'), path.join(harnessDir, 'scripts/lib/json-field.sh'));
|
|
121
|
+
copyFile(path.join(runnerDir, 'adapters/shared/tmux-session.sh'), path.join(harnessDir, 'scripts/lib/tmux-session.sh'));
|
|
116
122
|
copyFile(path.join(runnerDir, 'adapters/shared/log-tui.mjs'), path.join(harnessDir, 'scripts/lib/log-tui.mjs'));
|
|
117
123
|
makeExecutableTree(path.join(harnessDir, 'scripts'));
|
|
118
124
|
fs.writeFileSync(path.join(harnessDir, 'installed-scripts.sha256'), `${dirContentHash(path.join(harnessDir, 'scripts'))}\n`);
|
|
@@ -138,6 +144,7 @@ const manifest = {
|
|
|
138
144
|
refreshBuild: `${harnessRel}/scripts/refresh-build.sh`,
|
|
139
145
|
reopenBrowser: `${harnessRel}/scripts/reopen-browser.sh`,
|
|
140
146
|
sidepanelToggle: `${harnessRel}/scripts/sidepanel-toggle.sh`,
|
|
147
|
+
reattach: `${harnessRel}/scripts/reattach.sh`,
|
|
141
148
|
},
|
|
142
149
|
patchedFiles: [],
|
|
143
150
|
backupDir: null,
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# reattach.sh — reload a live extension slot in place (no rebuild, no relaunch)
|
|
3
|
+
#
|
|
4
|
+
# Purpose:
|
|
5
|
+
# Quick-reuse leaf for `mm-harness launch` on an already-healthy extension slot:
|
|
6
|
+
# re-snapshots dist/chrome into the loaded runtime-dist so the running Chrome
|
|
7
|
+
# picks up watched code changes, then reloads the extension over CDP (service
|
|
8
|
+
# worker + extension pages) and re-opens the wallet. Optionally navigates a
|
|
9
|
+
# main tab to --start-url. Runs entirely against the EXISTING slot Chrome
|
|
10
|
+
# profile — it never stops the watcher, kills Chrome, or clears the webpack
|
|
11
|
+
# cache (that is the clean path in live.sh).
|
|
12
|
+
#
|
|
13
|
+
# Inputs (flags / env):
|
|
14
|
+
# --target <metamask-extension> (default $PWD; becomes the working dir)
|
|
15
|
+
# --cdp-port <port> (required, numeric) — the slot's CDP endpoint
|
|
16
|
+
# --watcher-port <port> (optional; informational only)
|
|
17
|
+
# --start-url <url> (optional; opened as a main tab beside the wallet)
|
|
18
|
+
# --ext-id <id> (optional; else resolved from the extension.id file or CDP)
|
|
19
|
+
# --dist-dir <rel> (default dist/chrome), --settle-ms <ms> (default 8000)
|
|
20
|
+
#
|
|
21
|
+
# Outputs:
|
|
22
|
+
# Progress on stderr. Exit 0 — reloaded; 1 — repo/reload failed; 2 — bad args
|
|
23
|
+
# / non-numeric port / CDP unreachable.
|
|
24
|
+
#
|
|
25
|
+
# Never touches: product source files; the webpack watcher; the Chrome process;
|
|
26
|
+
# other slots' browsers (single CDP port scope).
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
TARGET="$PWD"
|
|
30
|
+
CDP_PORT="${CDP_PORT:-}"
|
|
31
|
+
WATCHER_PORT="${WATCHER_PORT:-}"
|
|
32
|
+
START_URL="${EXTENSION_START_URL:-}"
|
|
33
|
+
EXT_ID="${EXT_ID:-}"
|
|
34
|
+
DIST_DIR="dist/chrome"
|
|
35
|
+
SETTLE_MS="8000"
|
|
36
|
+
|
|
37
|
+
require_value() { [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; }
|
|
38
|
+
while [ "$#" -gt 0 ]; do
|
|
39
|
+
case "$1" in
|
|
40
|
+
--target) require_value "$@"; TARGET="$2"; shift 2 ;;
|
|
41
|
+
--cdp-port) require_value "$@"; CDP_PORT="$2"; shift 2 ;;
|
|
42
|
+
--watcher-port) require_value "$@"; WATCHER_PORT="$2"; shift 2 ;;
|
|
43
|
+
--start-url) require_value "$@"; START_URL="$2"; shift 2 ;;
|
|
44
|
+
--ext-id) require_value "$@"; EXT_ID="$2"; shift 2 ;;
|
|
45
|
+
--dist-dir) require_value "$@"; DIST_DIR="$2"; shift 2 ;;
|
|
46
|
+
--settle-ms) require_value "$@"; SETTLE_MS="$2"; shift 2 ;;
|
|
47
|
+
-h|--help)
|
|
48
|
+
echo "Usage: reattach.sh --target <metamask-extension> --cdp-port <port> [--watcher-port <port>] [--start-url <url>] [--ext-id <id>] [--dist-dir <rel>] [--settle-ms <ms>]"
|
|
49
|
+
exit 0
|
|
50
|
+
;;
|
|
51
|
+
*) echo "reattach: unknown arg: $1" >&2; exit 2 ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
if [ -z "$CDP_PORT" ]; then
|
|
56
|
+
echo "reattach: --cdp-port is required" >&2
|
|
57
|
+
echo " Next: mm-harness launch --build (clean build + relaunch a fresh slot)" >&2
|
|
58
|
+
exit 2
|
|
59
|
+
fi
|
|
60
|
+
case "$CDP_PORT" in
|
|
61
|
+
*[!0-9]*) echo "reattach: --cdp-port must be numeric (got: $CDP_PORT)" >&2; exit 2 ;;
|
|
62
|
+
esac
|
|
63
|
+
|
|
64
|
+
if [ ! -f "$TARGET/package.json" ]; then
|
|
65
|
+
echo "reattach: --target is not a checkout (no package.json): $TARGET" >&2
|
|
66
|
+
echo " Next: mm-harness launch --target /path/to/metamask-extension --build" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
TARGET="$(cd "$TARGET" && pwd)"
|
|
70
|
+
|
|
71
|
+
if ! curl -s -m 3 "http://127.0.0.1:${CDP_PORT}/json/version" >/dev/null 2>&1; then
|
|
72
|
+
echo "reattach: CDP not reachable on port ${CDP_PORT}; the slot browser is not live." >&2
|
|
73
|
+
echo " Next: mm-harness launch --build (clean build + relaunch)" >&2
|
|
74
|
+
exit 2
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
78
|
+
DIST_ABS="$TARGET/$DIST_DIR"
|
|
79
|
+
# The running Chrome loads the runtime-dist snapshot (frozen at launch), not the
|
|
80
|
+
# live dist. Re-snapshot so the in-place reload serves the latest watched build.
|
|
81
|
+
RUNTIME_DIST_ABS="$TARGET/temp/recipe/runtime/runtime-dist"
|
|
82
|
+
|
|
83
|
+
if [ -f "$SCRIPT_DIR/snapshot-dist.sh" ] && [ -d "$RUNTIME_DIST_ABS" ]; then
|
|
84
|
+
echo "[reattach] re-snapshotting dist -> runtime-dist for in-place reload" >&2
|
|
85
|
+
bash "$SCRIPT_DIR/snapshot-dist.sh" --dist "$DIST_ABS" --runtime-dist "$RUNTIME_DIST_ABS" >&2 || {
|
|
86
|
+
echo "reattach: dist re-snapshot failed; the loaded extension may be mid-rebuild." >&2
|
|
87
|
+
echo " Next: mm-harness launch --build (clean build + relaunch)" >&2
|
|
88
|
+
exit 1
|
|
89
|
+
}
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Resolve the extension id: explicit flag, then the recorded id file, else CDP.
|
|
93
|
+
if [ -z "$EXT_ID" ]; then
|
|
94
|
+
for idf in "$TARGET/temp/recipe/runtime/extension.id" "$SCRIPT_DIR/extension.id"; do
|
|
95
|
+
if [ -f "$idf" ]; then
|
|
96
|
+
EXT_ID="$(tr -d '[:space:]' < "$idf")"
|
|
97
|
+
[ -n "$EXT_ID" ] && break
|
|
98
|
+
fi
|
|
99
|
+
done
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
cd "$TARGET"
|
|
103
|
+
echo "[reattach] reloading extension in place over CDP :${CDP_PORT}" >&2
|
|
104
|
+
|
|
105
|
+
CDP_PORT="$CDP_PORT" EXT_ID="$EXT_ID" START_URL="$START_URL" SETTLE_MS="$SETTLE_MS" node <<'NODE'
|
|
106
|
+
let chromium; try { chromium = require('@playwright/test').chromium; } catch { chromium = require('playwright').chromium; }
|
|
107
|
+
|
|
108
|
+
const port = process.env.CDP_PORT;
|
|
109
|
+
const startUrl = process.env.START_URL || '';
|
|
110
|
+
const settleMs = Number(process.env.SETTLE_MS || '8000') || 8000;
|
|
111
|
+
|
|
112
|
+
function extensionIdFrom(context, provided) {
|
|
113
|
+
if (provided) return provided;
|
|
114
|
+
const urls = [];
|
|
115
|
+
for (const page of context.pages()) urls.push(page.url());
|
|
116
|
+
for (const worker of context.serviceWorkers()) urls.push(worker.url());
|
|
117
|
+
for (const url of urls) {
|
|
118
|
+
const match = /^chrome-extension:\/\/([^/]+)\//u.exec(url);
|
|
119
|
+
if (match) return match[1];
|
|
120
|
+
}
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function connect() {
|
|
125
|
+
const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
|
|
126
|
+
const context = browser.contexts()[0];
|
|
127
|
+
return { browser, context };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
(async () => {
|
|
131
|
+
// Phase 1: trigger a full extension reload (service worker + all pages) from an
|
|
132
|
+
// extension page so Chrome re-reads the freshly snapshotted runtime-dist.
|
|
133
|
+
let { browser, context } = await connect();
|
|
134
|
+
const extId = extensionIdFrom(context, process.env.EXT_ID);
|
|
135
|
+
if (!extId) {
|
|
136
|
+
await browser.close();
|
|
137
|
+
console.error('reattach: could not resolve the extension id from the live CDP targets.');
|
|
138
|
+
console.error(' Next: mm-harness launch --build (clean build + relaunch)');
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let page = context
|
|
143
|
+
.pages()
|
|
144
|
+
.find((candidate) => candidate.url().startsWith(`chrome-extension://${extId}/`));
|
|
145
|
+
if (!page) {
|
|
146
|
+
page = await context.newPage();
|
|
147
|
+
await page.goto(`chrome-extension://${extId}/home.html`, {
|
|
148
|
+
waitUntil: 'domcontentloaded',
|
|
149
|
+
timeout: 15000,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
await page.evaluate(() => chrome.runtime.reload()).catch(() => {
|
|
153
|
+
// The reload tears the page context down; a rejected evaluate is expected.
|
|
154
|
+
});
|
|
155
|
+
await browser.close();
|
|
156
|
+
|
|
157
|
+
// Phase 2: wait for the extension to come back, then re-open the wallet and,
|
|
158
|
+
// when requested, a dapp tab. Chrome itself never restarted, so CDP stays up.
|
|
159
|
+
const deadline = Date.now() + settleMs;
|
|
160
|
+
let back = false;
|
|
161
|
+
while (Date.now() < deadline) {
|
|
162
|
+
try {
|
|
163
|
+
const probe = await connect();
|
|
164
|
+
const id = extensionIdFrom(probe.context, extId);
|
|
165
|
+
await probe.browser.close();
|
|
166
|
+
if (id) { back = true; break; }
|
|
167
|
+
} catch {
|
|
168
|
+
// CDP momentarily unavailable during reload; retry until the deadline.
|
|
169
|
+
}
|
|
170
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
171
|
+
}
|
|
172
|
+
if (!back) {
|
|
173
|
+
console.error(`reattach: extension did not come back within ${settleMs}ms after reload.`);
|
|
174
|
+
console.error(' Next: mm-harness launch --build (clean build + relaunch)');
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
({ browser, context } = await connect());
|
|
179
|
+
let home = context
|
|
180
|
+
.pages()
|
|
181
|
+
.find(
|
|
182
|
+
(candidate) =>
|
|
183
|
+
candidate.url().startsWith(`chrome-extension://${extId}/`) &&
|
|
184
|
+
!candidate.url().includes('/sidepanel.html'),
|
|
185
|
+
);
|
|
186
|
+
if (!home) {
|
|
187
|
+
home = await context.newPage();
|
|
188
|
+
await home
|
|
189
|
+
.goto(`chrome-extension://${extId}/home.html`, { waitUntil: 'domcontentloaded', timeout: 15000 })
|
|
190
|
+
.catch(() => { /* wallet home is best-effort; the reload already applied */ });
|
|
191
|
+
}
|
|
192
|
+
if (startUrl) {
|
|
193
|
+
const dapp = context
|
|
194
|
+
.pages()
|
|
195
|
+
.find((candidate) => candidate.url().startsWith('http://') || candidate.url().startsWith('https://'));
|
|
196
|
+
const tab = dapp || (await context.newPage());
|
|
197
|
+
await tab
|
|
198
|
+
.goto(startUrl, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
|
199
|
+
.catch(() => { /* dapp navigation is best-effort */ });
|
|
200
|
+
}
|
|
201
|
+
await browser.close();
|
|
202
|
+
})().catch((error) => {
|
|
203
|
+
console.error(`reattach: ${error && error.message ? error.message : error}`);
|
|
204
|
+
console.error(' Next: mm-harness launch --build (clean build + relaunch)');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
207
|
+
NODE
|
|
208
|
+
|
|
209
|
+
echo "[reattach] extension reloaded in place — no rebuild, no relaunch" >&2
|
|
210
|
+
echo " Next: mm-harness verify (confirm the reloaded runtime)" >&2
|
|
@@ -174,12 +174,6 @@ write_canonical_watch_pid() {
|
|
|
174
174
|
printf '%s\n' "$live_pid" > "$slot_watch_pid_file"
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
slot_number() {
|
|
178
|
-
local base
|
|
179
|
-
base="$(basename "$PWD")"
|
|
180
|
-
[[ "$base" =~ -([0-9]+)$ ]] && printf '%s\n' "${BASH_REMATCH[1]}"
|
|
181
|
-
}
|
|
182
|
-
|
|
183
177
|
# Explicit clean-build path: clear the target-scoped webpack cache before
|
|
184
178
|
# starting the harness-owned watcher. Faster incremental refresh stays on
|
|
185
179
|
# `mm-harness refresh`; this script is for proving a clean build works.
|
|
@@ -240,17 +234,17 @@ path_prefix="${REPO_NODE_PATH_PREFIX:-}"
|
|
|
240
234
|
nohup env -u BUNDLED_DEBUGPY_PATH ${path_prefix:+PATH="${path_prefix}${PATH}"} ${WATCHER_PORT:+PORT="$WATCHER_PORT"} yarn start > "$watch_log" 2>&1 &
|
|
241
235
|
started_pid=$!
|
|
242
236
|
printf '%s\n' "$started_pid" > "$watch_pid_file"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if
|
|
253
|
-
tmux_session="$(
|
|
237
|
+
# The orchestrator names the session; the harness only populates windows. Resolve
|
|
238
|
+
# via the run-owned ladder (RECIPE_TMUX_SESSION → agentic-runtime.json session →
|
|
239
|
+
# current tmux client) — no slot-number derivation.
|
|
240
|
+
tmux_session=""
|
|
241
|
+
for _tv_src in "$SCRIPT_DIR/lib/tmux-session.sh" "$SCRIPT_DIR/../shared/tmux-session.sh"; do
|
|
242
|
+
# shellcheck disable=SC1091
|
|
243
|
+
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
244
|
+
done
|
|
245
|
+
unset _tv_src
|
|
246
|
+
if command -v resolve_run_tmux_session >/dev/null 2>&1; then
|
|
247
|
+
tmux_session="$(resolve_run_tmux_session "$RUNTIME_DIR")"
|
|
254
248
|
fi
|
|
255
249
|
if [ -n "$tmux_session" ] && command -v tmux >/dev/null 2>&1 && tmux has-session -t "$tmux_session" 2>/dev/null; then
|
|
256
250
|
tmux_window="webpack-${WATCHER_PORT:-default}"
|
package/adapters/manifest.json
CHANGED
|
@@ -178,6 +178,14 @@
|
|
|
178
178
|
"inputs": "<status|open|close|toggle|cycle> --cdp-port --ext-id --agent-dir --settle-ms; env REPO, CDP_PORT, EXT_ID, SETTLE_MS, AGENT_DIR",
|
|
179
179
|
"outputs": "status/progress on stdout; exit 0/1/2"
|
|
180
180
|
},
|
|
181
|
+
{
|
|
182
|
+
"id": "extension/reattach",
|
|
183
|
+
"entry": "adapters/extension/reattach.sh",
|
|
184
|
+
"kind": "bash",
|
|
185
|
+
"purpose": "Reload a live extension slot in place: re-snapshot dist into the loaded runtime-dist, then reload the extension (service worker + pages) over CDP — no watcher stop, no Chrome kill.",
|
|
186
|
+
"inputs": "--target --cdp-port --watcher-port --start-url --ext-id --dist-dir --settle-ms; env CDP_PORT, WATCHER_PORT, EXTENSION_START_URL, EXT_ID",
|
|
187
|
+
"outputs": "progress on stderr; exit 0/1/2"
|
|
188
|
+
},
|
|
181
189
|
{
|
|
182
190
|
"id": "extension/pin-remote-flags",
|
|
183
191
|
"entry": "adapters/extension/pin-remote-flags.cjs",
|
|
@@ -290,6 +298,14 @@
|
|
|
290
298
|
"inputs": "json path + dotted field",
|
|
291
299
|
"outputs": "field value on stdout"
|
|
292
300
|
},
|
|
301
|
+
{
|
|
302
|
+
"id": "lib/tmux-session",
|
|
303
|
+
"entry": "adapters/shared/tmux-session.sh",
|
|
304
|
+
"kind": "lib",
|
|
305
|
+
"purpose": "resolve_run_tmux_session: the run-owned tmux session a log-tail window belongs in (RECIPE_TMUX_SESSION -> agentic-runtime.json session -> current client; no slot-number derivation).",
|
|
306
|
+
"inputs": "resolve_run_tmux_session [runtime-dir]; env RECIPE_TMUX_SESSION, RECIPE_RUNTIME_CONTEXT, TMUX",
|
|
307
|
+
"outputs": "resolved session name on stdout (empty when none)"
|
|
308
|
+
},
|
|
293
309
|
{
|
|
294
310
|
"id": "lib/activate-repo-node",
|
|
295
311
|
"entry": "adapters/shared/activate-repo-node.sh",
|
|
@@ -298,6 +314,14 @@
|
|
|
298
314
|
"inputs": "activate_repo_node [target-dir]; tries asdf, fnm, mise, nvm, then matching node on PATH",
|
|
299
315
|
"outputs": "REPO_NODE_BIN, REPO_NODE_PATH_PREFIX; prepends bin dir to PATH; exit 0/1/2"
|
|
300
316
|
},
|
|
317
|
+
{
|
|
318
|
+
"id": "lib/activate-repo-ruby",
|
|
319
|
+
"entry": "adapters/shared/activate-repo-ruby.sh",
|
|
320
|
+
"kind": "lib",
|
|
321
|
+
"purpose": "Manager-agnostic Ruby pin from .ruby-version / .tool-versions for iOS pod/gem work in non-interactive spawns; best-effort variant warns + teaches when unresolved.",
|
|
322
|
+
"inputs": "activate_repo_ruby [target-dir] (0/1/2); activate_repo_ruby_best_effort [target-dir]; tries asdf, rbenv, mise, then matching ruby on PATH",
|
|
323
|
+
"outputs": "REPO_RUBY_BIN, REPO_RUBY_PATH_PREFIX; prepends bin dir to PATH"
|
|
324
|
+
},
|
|
301
325
|
{
|
|
302
326
|
"id": "lib/ensure-runner-deps",
|
|
303
327
|
"entry": "adapters/shared/ensure-runner-deps.sh",
|
|
@@ -306,21 +330,37 @@
|
|
|
306
330
|
"inputs": "ensure-runner-deps.sh [RUNNER_DIR]; env FARMSLOT_ROOT / METAMASK_RUNNER_PROTOCOL_ROOT optional local link target",
|
|
307
331
|
"outputs": "installed node_modules in RUNNER_DIR; exit 0 when deps ready"
|
|
308
332
|
},
|
|
333
|
+
{
|
|
334
|
+
"id": "lib/resolve-slot-ports",
|
|
335
|
+
"entry": "adapters/shared/resolve-slot-ports.mjs",
|
|
336
|
+
"kind": "node",
|
|
337
|
+
"purpose": "Resolve CDP + watcher ports from the orchestrator's slot-pool JSON by repo path, else 6660+N / 9010+N.",
|
|
338
|
+
"inputs": "resolve-slot-ports.mjs resolve_extension_runtime_ports <repo>",
|
|
339
|
+
"outputs": "CDP_PORT / WATCHER_PORT / SLOT_ID lines on stdout"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"id": "lib/resolve-slot-ports-core",
|
|
343
|
+
"entry": "adapters/shared/resolve-slot-ports-core.mjs",
|
|
344
|
+
"kind": "lib",
|
|
345
|
+
"purpose": "Self-contained pool/context/formula port resolution imported by the node leaf and TS surface.",
|
|
346
|
+
"inputs": "ESM import from resolve-slot-ports.mjs or resolve-slot-ports.ts",
|
|
347
|
+
"outputs": "resolveSlotPortsByRepo and related functions"
|
|
348
|
+
},
|
|
309
349
|
{
|
|
310
350
|
"id": "lib/resolve-farmslot-ports",
|
|
311
351
|
"entry": "adapters/shared/resolve-farmslot-ports.mjs",
|
|
312
352
|
"kind": "node",
|
|
313
|
-
"purpose": "
|
|
314
|
-
"inputs": "resolve-farmslot-ports.mjs
|
|
315
|
-
"outputs": "
|
|
353
|
+
"purpose": "Back-compat shim (one release): delegates to resolve-slot-ports.mjs.",
|
|
354
|
+
"inputs": "resolve-farmslot-ports.mjs <fn> <repo>",
|
|
355
|
+
"outputs": "same as resolve-slot-ports.mjs"
|
|
316
356
|
},
|
|
317
357
|
{
|
|
318
358
|
"id": "lib/resolve-farmslot-ports-core",
|
|
319
359
|
"entry": "adapters/shared/resolve-farmslot-ports-core.mjs",
|
|
320
360
|
"kind": "lib",
|
|
321
|
-
"purpose": "
|
|
322
|
-
"inputs": "ESM import
|
|
323
|
-
"outputs": "
|
|
361
|
+
"purpose": "Back-compat shim (one release): re-exports resolve-slot-ports-core.mjs.",
|
|
362
|
+
"inputs": "ESM import of the old path",
|
|
363
|
+
"outputs": "re-exports resolveSlotPortsByRepo (and the resolveFarmslotPortsByRepo alias)"
|
|
324
364
|
},
|
|
325
365
|
{
|
|
326
366
|
"id": "lib/recipe-harness-root",
|
|
@@ -331,13 +371,21 @@
|
|
|
331
371
|
"outputs": "recipeHarnessRoot() relative path string"
|
|
332
372
|
},
|
|
333
373
|
{
|
|
334
|
-
"id": "lib/resolve-
|
|
335
|
-
"entry": "adapters/shared/resolve-
|
|
374
|
+
"id": "lib/resolve-slot-ports-sh",
|
|
375
|
+
"entry": "adapters/shared/resolve-slot-ports.sh",
|
|
336
376
|
"kind": "lib",
|
|
337
|
-
"purpose": "Bash-compat sourced wrapper over resolve-
|
|
377
|
+
"purpose": "Bash-compat sourced wrapper over resolve-slot-ports.mjs for stop-metro and legacy callers.",
|
|
338
378
|
"inputs": "source + resolve_* / apply_resolved_* function calls",
|
|
339
379
|
"outputs": "KEY=VALUE lines on stdout; env vars via apply_resolved_*"
|
|
340
380
|
},
|
|
381
|
+
{
|
|
382
|
+
"id": "lib/resolve-farmslot-ports-sh",
|
|
383
|
+
"entry": "adapters/shared/resolve-farmslot-ports.sh",
|
|
384
|
+
"kind": "lib",
|
|
385
|
+
"purpose": "Back-compat shim (one release): sources resolve-slot-ports.sh (keeps the resolve_farmslot_ports_by_repo alias).",
|
|
386
|
+
"inputs": "source",
|
|
387
|
+
"outputs": "same sourced functions as resolve-slot-ports.sh"
|
|
388
|
+
},
|
|
341
389
|
{
|
|
342
390
|
"id": "lib/reap-checkout-metros",
|
|
343
391
|
"entry": "adapters/shared/reap-checkout-metros.sh",
|
|
@@ -351,6 +399,14 @@
|
|
|
351
399
|
"inputs": "--target <checkout> --slot-id <id> --cdp-port <port>",
|
|
352
400
|
"outputs": "wallet-fixture.json in runtime dir; exit 0 always (non-zero = informational, no source found)"
|
|
353
401
|
},
|
|
402
|
+
{
|
|
403
|
+
"id": "lib/open-log-window",
|
|
404
|
+
"entry": "adapters/shared/open-log-window.sh",
|
|
405
|
+
"kind": "bash",
|
|
406
|
+
"purpose": "(Re)open a read-only tmux tail window for a dev-server log in the run-owned session without touching the process (mm-harness logs --window).",
|
|
407
|
+
"inputs": "--window <name> --log <path> [--runtime-dir <dir>]",
|
|
408
|
+
"outputs": "tmux tail window in the resolved session; exit 0 opened / 1 no session or log / 2 bad args"
|
|
409
|
+
},
|
|
354
410
|
{
|
|
355
411
|
"id": "lib/path-defaults",
|
|
356
412
|
"entry": "adapters/shared/path-defaults.json",
|
|
@@ -13,24 +13,45 @@
|
|
|
13
13
|
# Inputs (env/args): $1 = metro log path; PORT, LOG_DIR, and optional `slot` from
|
|
14
14
|
# the caller; TMUX indicates we are inside a tmux client.
|
|
15
15
|
|
|
16
|
+
# Session resolution (RECIPE_TMUX_SESSION → agentic-runtime.json session → current
|
|
17
|
+
# tmux client). Sourced relative to this lib; harness-path fallback for the
|
|
18
|
+
# installed layout where shared libs live beside this file.
|
|
19
|
+
_tv_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
# shellcheck disable=SC1091
|
|
21
|
+
for _tv_src in "$_tv_dir/tmux-session.sh" "$_tv_dir/../../shared/tmux-session.sh"; do
|
|
22
|
+
[ -f "$_tv_src" ] && { . "$_tv_src"; break; }
|
|
23
|
+
done
|
|
24
|
+
unset _tv_src _tv_dir
|
|
25
|
+
|
|
16
26
|
# shellcheck disable=SC2329 # sourced by start-metro.sh; also sourced by tests.
|
|
17
27
|
start_viewer_window() {
|
|
18
28
|
local metro_log="$1"
|
|
19
29
|
command -v tmux >/dev/null 2>&1 || return 0
|
|
20
30
|
local window="metro-${PORT}"
|
|
21
31
|
local metro_tmux="$LOG_DIR/metro.tmux"
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if [ -n "${slot:-}" ] && tmux has-session -t "=mm-${slot}" 2>/dev/null; then
|
|
27
|
-
session="mm-${slot}"
|
|
28
|
-
# (2) the current session, but ONLY when we are actually inside a tmux client.
|
|
29
|
-
elif [ -n "${TMUX:-}" ]; then
|
|
30
|
-
session="$(tmux display-message -p '#S' 2>/dev/null || true)"
|
|
31
|
-
fi
|
|
32
|
+
# The orchestrator names the session; the harness only populates windows. No
|
|
33
|
+
# slot-number derivation — the name comes from the run-owned session ladder.
|
|
34
|
+
local session
|
|
35
|
+
session="$(resolve_run_tmux_session "$LOG_DIR")"
|
|
32
36
|
# No run-owned session → skip the viewer; no orphaned tail-window is left behind.
|
|
33
37
|
{ [ -n "$session" ] && tmux has-session -t "=$session" 2>/dev/null; } || return 0
|
|
38
|
+
# Hygiene: close stale metro-<port> windows in this session whose port has no
|
|
39
|
+
# live listener — a previous run's window left tailing a dead log. The current
|
|
40
|
+
# window is (re)created below, so skip it here.
|
|
41
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
42
|
+
tmux list-windows -t "$session" -F '#{window_name}' 2>/dev/null | while IFS= read -r _win; do
|
|
43
|
+
[ "$_win" = "$window" ] && continue
|
|
44
|
+
case "$_win" in
|
|
45
|
+
metro-*) : ;;
|
|
46
|
+
*) continue ;;
|
|
47
|
+
esac
|
|
48
|
+
_p="${_win#metro-}"
|
|
49
|
+
case "$_p" in ''|*[!0-9]*) continue ;; esac
|
|
50
|
+
if ! lsof -nP -iTCP:"$_p" -sTCP:LISTEN -t >/dev/null 2>&1; then
|
|
51
|
+
tmux kill-window -t "${session}:${_win}" >/dev/null 2>&1 || true
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
fi
|
|
34
55
|
tmux kill-window -t "${session}:${window}" >/dev/null 2>&1 || true
|
|
35
56
|
tmux new-window -d -t "$session" -n "$window" "exec tail -n +1 -F $(printf '%q' "$metro_log")"
|
|
36
57
|
printf '%s:%s\n' "$session" "$window" > "$metro_tmux"
|
|
@@ -51,6 +51,15 @@ esac
|
|
|
51
51
|
|
|
52
52
|
TARGET="$(cd "$TARGET" && pwd)"
|
|
53
53
|
|
|
54
|
+
# Pin the repo's Ruby for the iOS native build (pod/gem work): a cold checkout
|
|
55
|
+
# under nohup/tmux otherwise uses the system Ruby and fails native gem builds.
|
|
56
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
57
|
+
# shellcheck disable=SC1091
|
|
58
|
+
for _rb in "$SCRIPT_DIR/../shared/activate-repo-ruby.sh" "$SCRIPT_DIR/lib/activate-repo-ruby.sh"; do
|
|
59
|
+
[ -f "$_rb" ] && { . "$_rb"; break; }
|
|
60
|
+
done
|
|
61
|
+
unset _rb
|
|
62
|
+
|
|
54
63
|
# --- iOS helpers --------------------------------------------------------------
|
|
55
64
|
|
|
56
65
|
ios_app_installed() {
|
|
@@ -61,7 +70,11 @@ ios_app_installed() {
|
|
|
61
70
|
run_ios_native_build() {
|
|
62
71
|
local sim_target="$1" reason="$2"
|
|
63
72
|
printf '%s; installing with yarn start:ios (WATCHER_PORT=%s)\n' "$reason" "$PORT" >&2
|
|
64
|
-
(
|
|
73
|
+
(
|
|
74
|
+
cd "$TARGET"
|
|
75
|
+
command -v activate_repo_ruby_best_effort >/dev/null 2>&1 && activate_repo_ruby_best_effort "$TARGET"
|
|
76
|
+
WATCHER_PORT="$PORT" METRO_PORT="$PORT" IOS_SIMULATOR="$sim_target" EXPO_NO_TYPESCRIPT_SETUP=1 yarn start:ios 2>&1
|
|
77
|
+
)
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
ensure_ios_app_for_mode() {
|
|
@@ -35,7 +35,7 @@ fi
|
|
|
35
35
|
# pool/formula fallbacks) so we stop THIS slot's Metro, not the 8081 default.
|
|
36
36
|
if [ -z "$PORT" ]; then
|
|
37
37
|
# shellcheck disable=SC1091
|
|
38
|
-
. "$SCRIPT_DIR/../shared/resolve-
|
|
38
|
+
. "$SCRIPT_DIR/../shared/resolve-slot-ports.sh"
|
|
39
39
|
resolved_port="$(resolve_mobile_runtime_ports "$TARGET" 2>/dev/null | sed -n 's/^WATCHER_PORT=//p' | head -1 || true)"
|
|
40
40
|
PORT="${resolved_port:-8081}"
|
|
41
41
|
fi
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
# no native). The Metro-only remedy: native builds come from open-device.
|
|
13
13
|
set -euo pipefail
|
|
14
14
|
|
|
15
|
+
# Resolve SCRIPT_DIR before any cd (the script may be invoked by a relative path).
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
|
|
15
18
|
TARGET="$PWD"
|
|
16
19
|
MODE="full"
|
|
17
20
|
while [ "$#" -gt 0 ]; do
|
|
@@ -25,8 +28,19 @@ done
|
|
|
25
28
|
|
|
26
29
|
TARGET="$(cd "$TARGET" && pwd)"
|
|
27
30
|
cd "$TARGET"
|
|
31
|
+
|
|
32
|
+
# full mode runs native pods/jetify → pin the repo's Ruby so a cold checkout does
|
|
33
|
+
# not build iOS gems against the system Ruby. Best-effort (never blocks the setup).
|
|
34
|
+
# shellcheck disable=SC1091
|
|
35
|
+
for _rb in "$SCRIPT_DIR/../shared/activate-repo-ruby.sh" "$SCRIPT_DIR/lib/activate-repo-ruby.sh"; do
|
|
36
|
+
[ -f "$_rb" ] && { . "$_rb"; break; }
|
|
37
|
+
done
|
|
38
|
+
unset _rb
|
|
39
|
+
|
|
28
40
|
case "$MODE" in
|
|
29
|
-
full)
|
|
41
|
+
full)
|
|
42
|
+
command -v activate_repo_ruby_best_effort >/dev/null 2>&1 && activate_repo_ruby_best_effort "$TARGET"
|
|
43
|
+
exec yarn setup ;;
|
|
30
44
|
expo) exec yarn setup:expo --no-build-ios --no-build-android ;;
|
|
31
45
|
*) printf 'yarn-setup: unknown --mode: %s (want full|expo)\n' "$MODE" >&2; exit 2 ;;
|
|
32
46
|
esac
|