@deeeed/metamask-harness 0.17.5 → 0.18.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 +872 -0
- package/README.md +94 -140
- package/adapters/extension/console-tail.mjs +55 -24
- package/adapters/extension/ensure-browser.sh +6 -1
- package/adapters/extension/inject.mjs +6 -9
- 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 +1 -1
- 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 +90 -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 +19 -64
- package/adapters/mobile/wait-for-bridge.sh +22 -8
- 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/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 -14
- package/dist/command-contract.js +451 -0
- package/dist/command-journal.js +225 -0
- package/dist/commands/call.js +170 -50
- package/dist/commands/check.js +9 -3
- package/dist/commands/completion-candidates.js +20 -13
- package/dist/commands/device-target.js +27 -12
- package/dist/commands/doctor.js +106 -25
- package/dist/commands/fixtures.js +92 -34
- package/dist/commands/flows.js +39 -10
- package/dist/commands/last.js +52 -0
- package/dist/commands/launch/extension.js +38 -15
- package/dist/commands/launch/index.js +207 -63
- package/dist/commands/list-executables.js +151 -29
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +270 -35
- package/dist/commands/parse-args.js +13 -1
- package/dist/commands/provision.js +10 -3
- package/dist/commands/run-engine.js +435 -105
- package/dist/commands/run-report.js +12 -3
- package/dist/commands/run.js +355 -50
- package/dist/commands/shared.js +75 -3
- 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 +2 -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 +135 -54
- package/dist/paths.js +2 -5
- package/dist/recipe-security.js +178 -0
- package/dist/run-diagnostics.js +261 -0
- package/dist/runner.js +117 -8
- package/docs/CONTRIBUTING.md +137 -0
- package/docs/QA.md +185 -0
- package/docs/RECIPES.md +161 -0
- package/docs/SECURITY.md +88 -0
- package/library/README.md +4 -0
- package/library/actions/core/perps/_controller.mjs +10 -55
- 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/platform/cdp.mjs +1 -0
- package/library/actions/extension/wallet/list_accounts.mjs +41 -0
- package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
- package/library/manifests/core.action-manifest.json +68 -7
- package/library/manifests/extension.action-manifest.json +53 -0
- package/library/manifests/mobile.action-manifest.json +81 -3
- package/library/recipes/runner/action-validation.extension.recipe.json +8 -1
- package/library/recipes/runner/action-validation.mobile.recipe.json +8 -1
- package/library/recipes/runner/smoke.core.recipe.json +27 -0
- package/library/recipes/wallet/smoke.extension.recipe.json +42 -0
- package/library/recipes/wallet/smoke.mobile.recipe.json +42 -0
- package/package.json +12 -5
- package/scripts/completions.sh +7 -7
- 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/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/adapters/manifest.json
CHANGED
|
@@ -18,6 +18,22 @@
|
|
|
18
18
|
"inputs": "--target --port --clear; env WATCHER_PORT, METRO_PORT, MOBILE_METRO_REQUIRED_ENV, METRO_MAX_WORKERS",
|
|
19
19
|
"outputs": "Metro PID file + log; exit 0 ready / 1 timeout / 2 bad args"
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
"id": "mobile/start-console-forwarder",
|
|
23
|
+
"entry": "adapters/mobile/start-console-forwarder.sh",
|
|
24
|
+
"kind": "bash",
|
|
25
|
+
"purpose": "Ensure one runner-owned Mobile application console collector is alive after bridge readiness.",
|
|
26
|
+
"inputs": "--target --port --quiet; env WATCHER_PORT, METRO_PORT, METAMASK_RECIPE_CONSOLE_FORWARD",
|
|
27
|
+
"outputs": "app-console.log + collector PID file; exit 0 ready / 1 startup failed / 2 bad args"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "mobile/launch-console-forwarder",
|
|
31
|
+
"entry": "adapters/mobile/launch-console-forwarder.cjs",
|
|
32
|
+
"kind": "node",
|
|
33
|
+
"purpose": "Detach the Mobile application console collector from command-runner process-group cleanup.",
|
|
34
|
+
"inputs": "--port --out --err",
|
|
35
|
+
"outputs": "collector PID on stdout; exit 0 launched / 1 spawn failed / 2 bad args"
|
|
36
|
+
},
|
|
21
37
|
{
|
|
22
38
|
"id": "mobile/launch-metro",
|
|
23
39
|
"entry": "adapters/mobile/launch-metro.cjs",
|
|
@@ -62,17 +78,17 @@
|
|
|
62
78
|
"id": "mobile/inject",
|
|
63
79
|
"entry": "adapters/mobile/inject.sh",
|
|
64
80
|
"kind": "bash",
|
|
65
|
-
"purpose": "Install
|
|
66
|
-
"inputs": "--target --
|
|
67
|
-
"outputs": "<harness>/manifest.json + runner/ + scripts
|
|
81
|
+
"purpose": "Install Mobile runner metadata, delegates, and snapshots without modifying product source.",
|
|
82
|
+
"inputs": "--target --no-git-exclude; env RECIPE_HARNESS_ROOT, METAMASK_RUNNER_PROTOCOL_ROOT",
|
|
83
|
+
"outputs": "<harness>/manifest.json + runner/ + scripts/; exit 0/1/2"
|
|
68
84
|
},
|
|
69
85
|
{
|
|
70
86
|
"id": "mobile/cleanup",
|
|
71
87
|
"entry": "adapters/mobile/cleanup.sh",
|
|
72
88
|
"kind": "bash",
|
|
73
|
-
"purpose": "Remove
|
|
74
|
-
"inputs": "--target
|
|
75
|
-
"outputs": "removes <harness
|
|
89
|
+
"purpose": "Remove Mobile harness metadata and recorded git excludes without touching product source.",
|
|
90
|
+
"inputs": "--target; env RECIPE_HARNESS_ROOT",
|
|
91
|
+
"outputs": "removes <harness>/; exit 0/1/2"
|
|
76
92
|
},
|
|
77
93
|
{
|
|
78
94
|
"id": "mobile/verify",
|
|
@@ -114,6 +130,30 @@
|
|
|
114
130
|
"inputs": "--target --runtime-dir --runner-bin --summary",
|
|
115
131
|
"outputs": "<runtime-dir>/recipe-harness-webpack.{pid,log}; optional summary; exit 0/1/2"
|
|
116
132
|
},
|
|
133
|
+
{
|
|
134
|
+
"id": "extension/launch-webpack",
|
|
135
|
+
"entry": "adapters/extension/launch-webpack.cjs",
|
|
136
|
+
"kind": "node",
|
|
137
|
+
"purpose": "Detach the normal yarn start watcher from the launching CLI process while preserving its target, environment, and log.",
|
|
138
|
+
"inputs": "--target --log --pid-file --sync-script --sync-pid-file --sync-log --dist --runtime-dist; optional --port --path-prefix",
|
|
139
|
+
"outputs": "watcher and runtime-sync pids; webpack and sync logs; exit 0/1"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "extension/sync-webpack-dist",
|
|
143
|
+
"entry": "adapters/extension/sync-webpack-dist.cjs",
|
|
144
|
+
"kind": "node",
|
|
145
|
+
"purpose": "After each successful webpack compile, update the loaded isolated runtime in place so an ordinary browser refresh sees the edit.",
|
|
146
|
+
"inputs": "--watcher-pid --watch-log --target --dist --runtime-dist --pid-file",
|
|
147
|
+
"outputs": "runtime-dist updated in place after successful compile markers; exits with its watcher"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "extension/stamp-runtime-title",
|
|
151
|
+
"entry": "adapters/extension/stamp-runtime-title.cjs",
|
|
152
|
+
"kind": "node",
|
|
153
|
+
"purpose": "Stamp the slot id into isolated Extension page titles so window identity survives browser refreshes.",
|
|
154
|
+
"inputs": "--target --runtime-dist; optional --runtime-dir",
|
|
155
|
+
"outputs": "home.html and sidepanel.html titles updated in the isolated runtime only; exit 0/1"
|
|
156
|
+
},
|
|
117
157
|
{
|
|
118
158
|
"id": "extension/stop-viewers",
|
|
119
159
|
"entry": "adapters/extension/stop-viewers.sh",
|
|
@@ -378,22 +418,6 @@
|
|
|
378
418
|
"inputs": "ESM import from resolve-slot-ports.mjs or resolve-slot-ports.ts",
|
|
379
419
|
"outputs": "resolveSlotPortsByRepo and related functions"
|
|
380
420
|
},
|
|
381
|
-
{
|
|
382
|
-
"id": "lib/resolve-farmslot-ports",
|
|
383
|
-
"entry": "adapters/shared/resolve-farmslot-ports.mjs",
|
|
384
|
-
"kind": "node",
|
|
385
|
-
"purpose": "Back-compat shim (one release): delegates to resolve-slot-ports.mjs.",
|
|
386
|
-
"inputs": "resolve-farmslot-ports.mjs <fn> <repo>",
|
|
387
|
-
"outputs": "same as resolve-slot-ports.mjs"
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
"id": "lib/resolve-farmslot-ports-core",
|
|
391
|
-
"entry": "adapters/shared/resolve-farmslot-ports-core.mjs",
|
|
392
|
-
"kind": "lib",
|
|
393
|
-
"purpose": "Back-compat shim (one release): re-exports resolve-slot-ports-core.mjs.",
|
|
394
|
-
"inputs": "ESM import of the old path",
|
|
395
|
-
"outputs": "re-exports resolveSlotPortsByRepo (and the resolveFarmslotPortsByRepo alias)"
|
|
396
|
-
},
|
|
397
421
|
{
|
|
398
422
|
"id": "lib/recipe-harness-root",
|
|
399
423
|
"entry": "adapters/shared/recipe-harness-root.mjs",
|
|
@@ -406,18 +430,10 @@
|
|
|
406
430
|
"id": "lib/resolve-slot-ports-sh",
|
|
407
431
|
"entry": "adapters/shared/resolve-slot-ports.sh",
|
|
408
432
|
"kind": "lib",
|
|
409
|
-
"purpose": "
|
|
433
|
+
"purpose": "Sourced wrapper over resolve-slot-ports.mjs for shell runtime leaves.",
|
|
410
434
|
"inputs": "source + resolve_* / apply_resolved_* function calls",
|
|
411
435
|
"outputs": "KEY=VALUE lines on stdout; env vars via apply_resolved_*"
|
|
412
436
|
},
|
|
413
|
-
{
|
|
414
|
-
"id": "lib/resolve-farmslot-ports-sh",
|
|
415
|
-
"entry": "adapters/shared/resolve-farmslot-ports.sh",
|
|
416
|
-
"kind": "lib",
|
|
417
|
-
"purpose": "Back-compat shim (one release): sources resolve-slot-ports.sh (keeps the resolve_farmslot_ports_by_repo alias).",
|
|
418
|
-
"inputs": "source",
|
|
419
|
-
"outputs": "same sourced functions as resolve-slot-ports.sh"
|
|
420
|
-
},
|
|
421
437
|
{
|
|
422
438
|
"id": "lib/reap-checkout-metros",
|
|
423
439
|
"entry": "adapters/shared/reap-checkout-metros.sh",
|
|
@@ -480,12 +496,12 @@
|
|
|
480
496
|
"outputs": "stable SHA-256 fingerprint"
|
|
481
497
|
},
|
|
482
498
|
{
|
|
483
|
-
"id": "
|
|
484
|
-
"entry": "adapters/
|
|
485
|
-
"kind": "
|
|
486
|
-
"purpose": "
|
|
487
|
-
"inputs": "
|
|
488
|
-
"outputs": "
|
|
499
|
+
"id": "lib/update-check-worker",
|
|
500
|
+
"entry": "adapters/shared/update-check-worker.mjs",
|
|
501
|
+
"kind": "node",
|
|
502
|
+
"purpose": "Detached, silent registry probe used by the daily passive update notice.",
|
|
503
|
+
"inputs": "<cache-file> <timeout-ms> [registry-endpoint]",
|
|
504
|
+
"outputs": "atomic update-cache refresh; always silent and best-effort"
|
|
489
505
|
}
|
|
490
506
|
]
|
|
491
507
|
}
|
|
@@ -254,6 +254,21 @@ const COMMANDS = {
|
|
|
254
254
|
return { ...result, testId, deviceName };
|
|
255
255
|
},
|
|
256
256
|
|
|
257
|
+
async 'press-text'(client, args, { deviceName } = {}) {
|
|
258
|
+
const text = args[0];
|
|
259
|
+
if (!text) {
|
|
260
|
+
throw new Error('Usage: press-text <text>');
|
|
261
|
+
}
|
|
262
|
+
const expr = `(function() {
|
|
263
|
+
if (typeof globalThis.__AGENTIC__?.pressText !== 'function') {
|
|
264
|
+
return { ok: false, error: 'The app bridge does not expose pressText' };
|
|
265
|
+
}
|
|
266
|
+
return globalThis.__AGENTIC__.pressText(${JSON.stringify(text)});
|
|
267
|
+
})()`;
|
|
268
|
+
const result = await cdpEval(client, expr);
|
|
269
|
+
return { ...result, text, deviceName };
|
|
270
|
+
},
|
|
271
|
+
|
|
257
272
|
async 'scroll-view'(client, args, { deviceName } = {}) {
|
|
258
273
|
let testId;
|
|
259
274
|
let offset = 300;
|
|
@@ -678,6 +693,7 @@ Commands:
|
|
|
678
693
|
get-selected-account Get the currently selected account
|
|
679
694
|
switch-account <address> Switch to account by address
|
|
680
695
|
press-test-id <testId> Press a component by its testID prop
|
|
696
|
+
press-text <text> Press a component containing visible text
|
|
681
697
|
scroll-view [--test-id <id>] [--offset <n>] [--animated]
|
|
682
698
|
Scroll a ScrollView/FlatList
|
|
683
699
|
set-input <testId> <value> Set text input value by testID (calls onChangeText)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// console-forwarder — stream device console lines into metro.log.
|
|
3
3
|
//
|
|
4
|
-
// React Native (Bridgeless) gates its
|
|
4
|
+
// React Native (Bridgeless) gates its built-in console→Metro forwarding on
|
|
5
5
|
// `console._isPolyfilled` (setUpDeveloperTools.js, T214991636); with the Hermes
|
|
6
6
|
// native console that gate is false, so app logs (incl. DevLogger) never reach
|
|
7
7
|
// Metro's log. The supported contract for log consumption in modern RN is CDP —
|
|
@@ -50,7 +50,9 @@ function matchesBridgeTarget(target, env) {
|
|
|
50
50
|
// (an in-app agentic bridge is live), not merely a registered debug target.
|
|
51
51
|
function hasMatchingRoute(value, env) {
|
|
52
52
|
const targets = Array.isArray(value) ? value : [value];
|
|
53
|
-
return targets.some(
|
|
53
|
+
return targets.some(
|
|
54
|
+
(t) => matchesBridgeTarget(t, env) && t && t.agenticPresent === true && Boolean(t.route),
|
|
55
|
+
);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
// Human summary of the targets that answered (platform / deviceName), for teaching on
|
|
@@ -136,7 +136,7 @@ async function discoverTarget(port) {
|
|
|
136
136
|
// ANDROID_TARGET_DEVICE_NAME — Metro-compatible model prefix resolved by bridge.mjs
|
|
137
137
|
// from the adb serial via `adb -s <serial> shell getprop ro.product.model`.
|
|
138
138
|
// Example: "Pixel 6" matches Metro deviceName "Pixel 6 - 16 - API 36".
|
|
139
|
-
// ANDROID_DEVICE — exact Metro deviceName
|
|
139
|
+
// ANDROID_DEVICE — exact user-specified Metro deviceName.
|
|
140
140
|
const androidTargetName = loadAndroidTargetDeviceName();
|
|
141
141
|
const androidDevice = loadAndroidDevice();
|
|
142
142
|
const adbSerial = process.env.ADB_SERIAL || process.env.ANDROID_SERIAL || '';
|
|
@@ -205,8 +205,7 @@ async function discoverTarget(port) {
|
|
|
205
205
|
}
|
|
206
206
|
candidates = androidFiltered;
|
|
207
207
|
} else if (androidDevice) {
|
|
208
|
-
// Exact Metro deviceName match
|
|
209
|
-
// legacy path where ANDROID_DEVICE was not resolved from serial).
|
|
208
|
+
// Exact Metro deviceName match when the user set ANDROID_DEVICE directly.
|
|
210
209
|
androidFiltered = candidates.filter((t) => t.deviceName === androidDevice);
|
|
211
210
|
if (androidFiltered.length === 0) {
|
|
212
211
|
// Pinned by exact Metro name but no candidate matched — fail fast.
|
|
@@ -1,196 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# Purpose:
|
|
5
|
-
# Restores backed-up product files (full installs), removes exactly the
|
|
6
|
-
# .git/info/exclude entries the install recorded, then deletes the harness
|
|
7
|
-
# dir and backup store. Metadata-only installs remove metadata alone.
|
|
8
|
-
#
|
|
9
|
-
# Inputs (flags / env):
|
|
10
|
-
# --target <metamask-mobile> (default $PWD)
|
|
11
|
-
# --allow-managed-changes skip the managed-hash refusal
|
|
12
|
-
# env RECIPE_HARNESS_ROOT
|
|
13
|
-
#
|
|
14
|
-
# Outputs:
|
|
15
|
-
# Removes <harness>/ and backup dir; restores managed product paths.
|
|
16
|
-
# Exit 0 — cleaned; 1 — no backup state / changed managed paths refusal /
|
|
17
|
-
# missing backup; 2 — bad args.
|
|
18
|
-
#
|
|
19
|
-
# Never touches: product files other than the recorded managed paths;
|
|
20
|
-
# .git/info/exclude lines it did not record; product-local cache dirs.
|
|
2
|
+
# Remove checkout-local Mobile harness metadata without touching product source.
|
|
21
3
|
set -euo pipefail
|
|
22
4
|
|
|
23
5
|
TARGET="$PWD"
|
|
24
|
-
ALLOW_MANAGED_CHANGES=false
|
|
25
6
|
while [ "$#" -gt 0 ]; do
|
|
26
7
|
case "$1" in
|
|
27
|
-
--target) TARGET="$2"; shift 2 ;;
|
|
28
|
-
|
|
29
|
-
-h|--help) echo "Usage: cleanup.sh [--target <metamask-mobile>] [--allow-managed-changes]"; exit 0 ;;
|
|
8
|
+
--target) [ "$#" -ge 2 ] || { echo "Missing value for --target" >&2; exit 2; }; TARGET="$2"; shift 2 ;;
|
|
9
|
+
-h|--help) echo "Usage: cleanup.sh [--target <metamask-mobile>]"; exit 0 ;;
|
|
30
10
|
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
31
11
|
esac
|
|
32
12
|
done
|
|
33
13
|
|
|
34
14
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
. "$SCRIPT_DIR/../shared/hash-helpers.sh"
|
|
38
|
-
# shellcheck disable=SC1091
|
|
39
|
-
for _hp in "$SCRIPT_DIR/../shared/harness-path.sh"; do
|
|
40
|
-
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
41
|
-
done
|
|
42
|
-
unset _hp
|
|
43
|
-
if ! command -v harness_root >/dev/null 2>&1; then
|
|
44
|
-
echo "mm-harness: shared lib adapters/shared/harness-path.sh not found; reinstall the runner." >&2
|
|
45
|
-
exit 1
|
|
46
|
-
fi
|
|
15
|
+
. "$SCRIPT_DIR/../shared/harness-path.sh"
|
|
16
|
+
TARGET="$(cd "$TARGET" && pwd -P)"
|
|
47
17
|
HARNESS_DIR="$(harness_dir "$TARGET" mobile)"
|
|
48
18
|
|
|
49
|
-
# A console-forwarder left running would keep polling for this checkout's Metro
|
|
50
|
-
# after cleanup orphans it; stop any forwarder scoped to this checkout first.
|
|
51
19
|
pkill -f "console-forwarder.cjs --port .* --out $TARGET/" 2>/dev/null || true
|
|
52
|
-
if GIT_BACKUP_PATH="$(git -C "$TARGET" rev-parse --git-path recipe-harness/mobile/backup 2>/dev/null)"; then
|
|
53
|
-
case "$GIT_BACKUP_PATH" in
|
|
54
|
-
/*) BACKUP_DIR="$GIT_BACKUP_PATH" ;;
|
|
55
|
-
*) BACKUP_DIR="$TARGET/$GIT_BACKUP_PATH" ;;
|
|
56
|
-
esac
|
|
57
|
-
else
|
|
58
|
-
BACKUP_DIR="$HARNESS_DIR/backup-store"
|
|
59
|
-
fi
|
|
60
|
-
if [ ! -f "$BACKUP_DIR/state.env" ] && [ -f "$HARNESS_DIR/backup/state.env" ]; then
|
|
61
|
-
BACKUP_DIR="$HARNESS_DIR/backup"
|
|
62
|
-
fi
|
|
63
|
-
STATE_FILE="$BACKUP_DIR/state.env"
|
|
64
|
-
|
|
65
|
-
remove_recorded_git_exclude_entries() {
|
|
66
|
-
local tracking_file="$1"
|
|
67
|
-
[ -s "$tracking_file" ] || return 0
|
|
68
|
-
local git_dir exclude_file tmp_file
|
|
69
|
-
if ! git_dir="$(git -C "$TARGET" rev-parse --git-dir 2>/dev/null)"; then
|
|
70
|
-
return 0
|
|
71
|
-
fi
|
|
72
|
-
case "$git_dir" in
|
|
73
|
-
/*) ;;
|
|
74
|
-
*) git_dir="$TARGET/$git_dir" ;;
|
|
75
|
-
esac
|
|
76
|
-
exclude_file="$git_dir/info/exclude"
|
|
77
|
-
[ -f "$exclude_file" ] || return 0
|
|
78
|
-
# Remove only the lines THIS install recorded, one occurrence per distinct
|
|
79
|
-
# ledger entry (the appended copy is the last match), so a pre-existing
|
|
80
|
-
# duplicate copy or a stale/duplicate ledger entry never drops a line we did
|
|
81
|
-
# not add this run.
|
|
82
|
-
tmp_file="$(mktemp)"
|
|
83
|
-
awk '
|
|
84
|
-
NR==FNR { if (length($0)) want[$0]=1; next }
|
|
85
|
-
{ lines[++n]=$0; if ($0 in want) last[$0]=n }
|
|
86
|
-
END { for (k in last) drop[last[k]]=1; for (i=1;i<=n;i++) if (!(i in drop)) print lines[i] }
|
|
87
|
-
' "$tracking_file" "$exclude_file" > "$tmp_file"
|
|
88
|
-
mv "$tmp_file" "$exclude_file"
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if [ -f "$HARNESS_DIR/manifest.json" ] && node -e '
|
|
92
|
-
const fs = require("fs");
|
|
93
|
-
const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
94
|
-
process.exit(manifest.installMode === "product-owned" ? 0 : 1);
|
|
95
|
-
' "$HARNESS_DIR/manifest.json" 2>/dev/null; then
|
|
96
|
-
remove_recorded_git_exclude_entries "$HARNESS_DIR/added-git-exclude"
|
|
97
|
-
rm -rf "$HARNESS_DIR"
|
|
98
|
-
echo "Cleaned mobile recipe harness metadata from $TARGET (product-owned in-app bridge files left untouched)"
|
|
99
|
-
exit 0
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
if [ ! -f "$STATE_FILE" ]; then
|
|
103
|
-
if [ -f "$HARNESS_DIR/manifest.json" ] && node -e '
|
|
104
|
-
const fs = require("fs");
|
|
105
|
-
const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
106
|
-
process.exit(manifest.installMode === "product-owned" ? 0 : 1);
|
|
107
|
-
' "$HARNESS_DIR/manifest.json" 2>/dev/null; then
|
|
108
|
-
remove_recorded_git_exclude_entries "$HARNESS_DIR/added-git-exclude"
|
|
109
|
-
rm -rf "$HARNESS_DIR"
|
|
110
|
-
echo "Cleaned mobile recipe harness metadata from $TARGET (product-owned in-app bridge files left untouched)"
|
|
111
|
-
exit 0
|
|
112
|
-
fi
|
|
113
|
-
echo "No mobile harness backup found at $STATE_FILE" >&2
|
|
114
|
-
exit 1
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
while IFS= read -r _line || [ -n "$_line" ]; do
|
|
118
|
-
[[ "$_line" =~ ^[[:space:]]*(#|$) ]] && continue
|
|
119
|
-
_key="${_line%%=*}"
|
|
120
|
-
_val="${_line#*=}"
|
|
121
|
-
case "$_key" in
|
|
122
|
-
AGENTIC_SERVICE_EXISTED|PACKAGE_JSON_EXISTED|NAVIGATION_SERVICE_EXISTED|APP_TSX_EXISTED) ;;
|
|
123
|
-
*) continue ;;
|
|
124
|
-
esac
|
|
125
|
-
export "$_key=$_val"
|
|
126
|
-
done < "$STATE_FILE"
|
|
127
|
-
unset _line _key _val
|
|
128
|
-
|
|
129
|
-
HASH_FILE="$BACKUP_DIR/managed-hashes.tsv"
|
|
130
|
-
|
|
131
|
-
verify_managed_paths_unchanged() {
|
|
132
|
-
if [ ! -f "$HASH_FILE" ]; then
|
|
133
|
-
cat >&2 <<EOF
|
|
134
|
-
Refusing cleanup: no managed hash file found at $HASH_FILE.
|
|
135
|
-
Re-run mobile harness install from the current runner to refresh safety metadata, or restore manually.
|
|
136
|
-
EOF
|
|
137
|
-
exit 1
|
|
138
|
-
fi
|
|
139
|
-
local rel expected actual conflicts=0
|
|
140
|
-
while IFS=$'\t' read -r rel expected; do
|
|
141
|
-
[ -n "$rel" ] || continue
|
|
142
|
-
actual="$(hash_path "$rel")"
|
|
143
|
-
if [ "$actual" != "$expected" ]; then
|
|
144
|
-
echo "Refusing cleanup: managed harness path changed after install: $rel" >&2
|
|
145
|
-
echo " expected: $expected" >&2
|
|
146
|
-
echo " actual: $actual" >&2
|
|
147
|
-
conflicts=1
|
|
148
|
-
fi
|
|
149
|
-
done < "$HASH_FILE"
|
|
150
|
-
if [ "$conflicts" != "0" ]; then
|
|
151
|
-
cat >&2 <<EOF
|
|
152
|
-
Cleanup would restore backups over files that changed after harness install.
|
|
153
|
-
Save/stash those changes, rerun harness install to refresh managed hashes, or restore manually.
|
|
154
|
-
EOF
|
|
155
|
-
exit 1
|
|
156
|
-
fi
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if [ "$ALLOW_MANAGED_CHANGES" != "true" ]; then
|
|
160
|
-
verify_managed_paths_unchanged
|
|
161
|
-
else
|
|
162
|
-
echo "Allowing cleanup over changed managed harness paths."
|
|
163
|
-
fi
|
|
164
20
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if [
|
|
172
|
-
|
|
173
|
-
|
|
21
|
+
tracking_file="$HARNESS_DIR/added-git-exclude"
|
|
22
|
+
if [ -s "$tracking_file" ]; then
|
|
23
|
+
git_dir="$(git -C "$TARGET" rev-parse --git-dir 2>/dev/null || true)"
|
|
24
|
+
if [ -n "$git_dir" ]; then
|
|
25
|
+
case "$git_dir" in /*) ;; *) git_dir="$TARGET/$git_dir" ;; esac
|
|
26
|
+
exclude_file="$git_dir/info/exclude"
|
|
27
|
+
if [ -f "$exclude_file" ]; then
|
|
28
|
+
tmp_file="$(mktemp)"
|
|
29
|
+
awk 'NR==FNR { if (length($0)) drop[$0]=1; next } !($0 in drop)' "$tracking_file" "$exclude_file" > "$tmp_file"
|
|
30
|
+
mv "$tmp_file" "$exclude_file"
|
|
174
31
|
fi
|
|
175
|
-
rm -rf "$target_path"
|
|
176
|
-
mkdir -p "$(dirname "$target_path")"
|
|
177
|
-
cp -a "$backup_path" "$target_path"
|
|
178
|
-
else
|
|
179
|
-
rm -rf "$target_path"
|
|
180
32
|
fi
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
restore_path "app/dev-tools/AgenticService" "${AGENTIC_SERVICE_EXISTED:-0}"
|
|
184
|
-
if [ "${PACKAGE_JSON_EXISTED+x}" = "x" ]; then
|
|
185
|
-
restore_path "package.json" "$PACKAGE_JSON_EXISTED"
|
|
186
33
|
fi
|
|
187
|
-
restore_path "app/core/NavigationService/NavigationService.ts" "${NAVIGATION_SERVICE_EXISTED:-0}"
|
|
188
|
-
restore_path "app/components/Nav/App/App.tsx" "${APP_TSX_EXISTED:-0}"
|
|
189
|
-
|
|
190
|
-
remove_recorded_git_exclude_entries "$BACKUP_DIR/added-git-exclude"
|
|
191
34
|
|
|
192
|
-
# Leave any product-local cache dirs alone: they are owned by the product
|
|
193
|
-
# checkout, not the harness.
|
|
194
35
|
rm -rf "$HARNESS_DIR"
|
|
195
|
-
|
|
196
|
-
echo "Cleaned mobile recipe harness from $TARGET"
|
|
36
|
+
echo "Cleaned mobile harness metadata from $TARGET; product files were not touched"
|