@deeeed/metamask-harness 0.5.0 → 0.6.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 (102) hide show
  1. package/CHANGELOG.md +51 -20
  2. package/adapters/core/inject.sh +1 -0
  3. package/adapters/extension/cleanup.mjs +1 -1
  4. package/adapters/extension/inject.mjs +4 -1
  5. package/adapters/extension/launch-browser.cjs +18 -4
  6. package/adapters/extension/live.sh +14 -2
  7. package/adapters/extension/refresh-build.sh +2 -2
  8. package/adapters/extension/sidepanel-toggle.sh +33 -12
  9. package/adapters/extension/start-watch.sh +2 -2
  10. package/adapters/extension/wallet-fixture-state.cjs +38 -24
  11. package/adapters/manifest.json +32 -3
  12. package/adapters/mobile/inject.sh +1 -0
  13. package/adapters/mobile/start-metro.sh +1 -1
  14. package/adapters/mobile/stop-metro.sh +20 -0
  15. package/adapters/mobile/verify.sh +1 -1
  16. package/adapters/shared/activate-repo-node.sh +1 -1
  17. package/adapters/shared/cli-ux.sh +44 -25
  18. package/adapters/shared/ensure-runner-deps.sh +30 -0
  19. package/adapters/shared/log-tui.mjs +4 -4
  20. package/adapters/shared/open-debug.mjs +1 -1
  21. package/adapters/shared/reap-checkout-metros.sh +53 -0
  22. package/adapters/shared/recipe-harness-root.mjs +23 -0
  23. package/adapters/shared/resolve-farmslot-ports-core.mjs +205 -0
  24. package/adapters/shared/resolve-farmslot-ports.mjs +20 -0
  25. package/adapters/shared/resolve-farmslot-ports.sh +19 -126
  26. package/bin/mm-harness +45 -4
  27. package/dist/adapters/extension/runtime.js +3 -1
  28. package/dist/adapters/mobile/provision.js +34 -1
  29. package/dist/adapters/mobile/runtime-decision.js +3 -1
  30. package/dist/adapters/resolve-farmslot-ports.js +22 -0
  31. package/dist/adapters/slot-ports.js +18 -29
  32. package/dist/cli.js +32 -1463
  33. package/dist/commands/call.js +183 -0
  34. package/dist/commands/completion-candidates.js +58 -0
  35. package/dist/commands/doctor.js +101 -0
  36. package/dist/commands/ensure-ready.js +24 -0
  37. package/dist/commands/flows.js +62 -0
  38. package/dist/commands/launch/extension.js +40 -0
  39. package/dist/commands/{launch.js → launch/index.js} +15 -47
  40. package/dist/commands/launch/mobile.js +10 -0
  41. package/dist/commands/manifest.js +72 -0
  42. package/dist/commands/parse-args.js +189 -0
  43. package/dist/commands/provision.js +136 -0
  44. package/dist/commands/resolve-extension.js +23 -0
  45. package/dist/commands/run-engine.js +341 -0
  46. package/dist/commands/run.js +217 -0
  47. package/dist/commands/runtime-decision.js +58 -0
  48. package/dist/commands/runtime-health.js +25 -0
  49. package/dist/commands/runtime-launch.js +139 -0
  50. package/dist/commands/self-test.js +52 -0
  51. package/dist/commands/stop.js +52 -0
  52. package/dist/harness.js +8 -48
  53. package/dist/mm-harness-cli.js +13 -8
  54. package/docs/CLI-SPEC.md +1 -1
  55. package/docs/CODE-MAP.md +62 -0
  56. package/library/README.md +14 -0
  57. package/library/actions/core/perps/_controller.mjs +1 -1
  58. package/library/actions/extension/platform/cdp.mjs +2 -2
  59. package/library/actions/extension/wallet/ensure_unlocked.mjs +7 -1
  60. package/library/actions/harness-exports.mjs +27 -0
  61. package/library/actions/mobile/wallet/ensure_unlocked.mjs +14 -2
  62. package/library/actions/mobile/wallet/setup.mjs +1 -1
  63. package/package.json +6 -7
  64. package/src/adapters/core/surface.ts +0 -71
  65. package/src/adapters/extension/ensure-ready.ts +0 -185
  66. package/src/adapters/extension/extension-id.ts +0 -107
  67. package/src/adapters/extension/runtime-decision.ts +0 -445
  68. package/src/adapters/extension/runtime.ts +0 -407
  69. package/src/adapters/extension/surface.ts +0 -88
  70. package/src/adapters/mobile/deps-markers.ts +0 -21
  71. package/src/adapters/mobile/prepare.ts +0 -246
  72. package/src/adapters/mobile/provision.ts +0 -594
  73. package/src/adapters/mobile/runtime-decision.ts +0 -459
  74. package/src/adapters/mobile/surface.ts +0 -71
  75. package/src/adapters/slot-ports.ts +0 -165
  76. package/src/adapters/surface.ts +0 -117
  77. package/src/adapters.ts +0 -601
  78. package/src/cli-color.ts +0 -92
  79. package/src/cli-commands.ts +0 -250
  80. package/src/cli-version.ts +0 -141
  81. package/src/cli.ts +0 -2091
  82. package/src/commands/debug.ts +0 -65
  83. package/src/commands/fixtures.ts +0 -198
  84. package/src/commands/launch.ts +0 -470
  85. package/src/commands/logs.ts +0 -99
  86. package/src/commands/shared.ts +0 -235
  87. package/src/commands/update.ts +0 -316
  88. package/src/completions-cache.ts +0 -86
  89. package/src/doctor.ts +0 -215
  90. package/src/harness.ts +0 -797
  91. package/src/heal-bounds.ts +0 -198
  92. package/src/index.ts +0 -15
  93. package/src/leaf-invoke.ts +0 -28
  94. package/src/live-adapter-contract.ts +0 -274
  95. package/src/manifest.ts +0 -47
  96. package/src/mm-harness-cli.ts +0 -655
  97. package/src/paths.ts +0 -198
  98. package/src/progress.ts +0 -117
  99. package/src/recording-target.ts +0 -147
  100. package/src/run-recording.ts +0 -329
  101. package/src/runner.ts +0 -108
  102. package/src/types.ts +0 -57
@@ -1,246 +0,0 @@
1
- // Mobile runtime prepare — in-process decision-and-action loop.
2
- //
3
- // Replaces the retired shell orchestrators (prepare-device.sh + launch.sh).
4
- // All sequencing is in TypeScript; leaf shell scripts handle single OS ops.
5
- // Control flows down only: TS → adapters/mobile/{yarn-setup,start-metro,open-device}.sh
6
- // No leaf script may call back into mm-harness (A1).
7
-
8
- import path from 'node:path';
9
-
10
- import { recordDepsBaseline } from '@farmslot/recipe-harness/runtime/deps-readiness';
11
-
12
- import { EXIT, spawnScriptStreaming } from '../../commands/shared.ts';
13
- import type { ScriptResult } from '../../commands/shared.ts';
14
- import { runnerDir } from '../../paths.ts';
15
- import {
16
- decideMobileReadiness,
17
- type MobileRuntimeDecisionReport,
18
- type MobileRuntimeDecisionOptions,
19
- } from './runtime-decision.ts';
20
-
21
- export { type MobileRuntimeDecisionReport };
22
-
23
- // Pod install (run by `yarn setup` and by the native `yarn start:ios|android`
24
- // build) shells `node --print require.resolve('react-native-worklets-core')` and
25
- // treats any output other than the exact string `undefined` as "module found".
26
- // An inherited FORCE_COLOR makes node emit a colorized `undefined`, so VisionCamera
27
- // misdetects the worklets pod, enables FrameProcessors, and fails on the missing
28
- // pod. Force plain output for pod-triggering spawns so the probe reads `undefined`.
29
- // CocoaPods reads UTF-8 podspecs; a parent env without a locale (gateway/tmux
30
- // spawns) makes Ruby parse them as US-ASCII and die on the first multibyte byte
31
- // ('"\xE2" on US-ASCII' in react-native-mmkv.podspec). Pin a UTF-8 locale for
32
- // pod-triggering spawns when the caller has none.
33
- const POD_PROBE_ENV: Record<string, string> = {
34
- FORCE_COLOR: '0',
35
- NO_COLOR: '1',
36
- LANG: process.env.LANG?.includes('UTF-8') ? process.env.LANG : 'en_US.UTF-8',
37
- LC_ALL: process.env.LC_ALL?.includes('UTF-8') ? process.env.LC_ALL : 'en_US.UTF-8',
38
- };
39
-
40
- export interface PrepareMobileOptions extends MobileRuntimeDecisionOptions {
41
- /**
42
- * Whether the caller emits a --json envelope on stdout. Leaf progress always
43
- * streams to stderr (see dispatchAction/spawnScriptStreaming); this only gates
44
- * the in-process teaching messages so they never pollute the stdout envelope.
45
- */
46
- json?: boolean;
47
- /**
48
- * Passed to open-device.sh --preflight-mode.
49
- * 'fast' = error if not installed; 'auto' = install if missing; 'rebuild-native'/'clean' = always rebuild.
50
- * Defaults to 'fast'.
51
- */
52
- preflightMode?: string;
53
- /**
54
- * Force --clear on start-metro (reset bundle cache).
55
- * Used by the setup-wallet clean-Metro retry path (M3).
56
- */
57
- clearMetro?: boolean;
58
- }
59
-
60
- /**
61
- * Read current runtime readiness without executing any action.
62
- * No side effects: just calls decideMobileReadiness and returns the report.
63
- */
64
- export async function mobileRuntimeStatus(
65
- target: string,
66
- opts: PrepareMobileOptions = {},
67
- ): Promise<MobileRuntimeDecisionReport> {
68
- return decideMobileReadiness(target, {
69
- watcherPort: opts.watcherPort,
70
- metroLog: opts.metroLog,
71
- platform: opts.platform,
72
- record: opts.record,
73
- preflightMode: opts.preflightMode,
74
- });
75
- }
76
-
77
- /**
78
- * Prepare the mobile runtime: run the readiness decision then execute each
79
- * action in sequence via single-purpose leaf shell scripts. Returns a
80
- * ScriptResult so the launch heal loop can classify outcomes uniformly.
81
- */
82
- export async function prepareMobile(
83
- target: string,
84
- opts: PrepareMobileOptions = {},
85
- ): Promise<ScriptResult> {
86
- const json = opts.json ?? false;
87
- const platform = opts.platform ?? 'ios';
88
- const preflightMode = opts.preflightMode ?? 'fast';
89
- const clearMetro = opts.clearMetro ?? false;
90
-
91
- const report = await decideMobileReadiness(target, {
92
- watcherPort: opts.watcherPort,
93
- metroLog: opts.metroLog,
94
- platform,
95
- record: opts.record,
96
- preflightMode,
97
- });
98
-
99
- if (report.decision === 'blocked') {
100
- const reasons = report.reasons.join(' ');
101
- const next = report.userAction ?? 'fix the bundle error in app code before retrying recipe up.';
102
- const msg = `mobile prepare blocked: ${reasons}\n Next: ${next}`;
103
- if (!json) process.stderr.write(`${msg}\n`);
104
- return { status: EXIT.runtime, output: msg };
105
- }
106
-
107
- if (report.decision === 'ready') {
108
- return { status: 0, output: '' };
109
- }
110
-
111
- if (report.decision === 'unknown') {
112
- const msg = 'mobile prepare: runtime state unknown\n Next: run mm-harness verify --adapter mobile --target <checkout>';
113
- if (!json) process.stderr.write(`${msg}\n`);
114
- return { status: EXIT.runtime, output: msg };
115
- }
116
-
117
- // decision is 'install' or 'launch' — execute each action in sequence.
118
- // clearMetro forces --clear on start-metro regardless of what decideMobileReadiness set.
119
- const actions = clearMetro
120
- ? report.actions.map((a) =>
121
- a.id === 'start-metro' && !a.argv?.includes('--clear')
122
- ? { ...a, argv: [...(a.argv ?? []), '--clear'] }
123
- : a,
124
- )
125
- : report.actions;
126
- for (const action of actions) {
127
- const result = await dispatchAction(action, target, platform, json, preflightMode);
128
- if (result.status !== 0) return result;
129
- // Record the deps baseline the instant a setup/install leaf succeeds, so the
130
- // mtime freshness fallback stops firing forever after one good install. This
131
- // is unconditional (not gated on a later re-decide or an env flag), so the
132
- // baseline lifecycle is self-sustaining regardless of the caller's path.
133
- if (action.id === 'yarn-setup') recordDepsBaseline(path.resolve(target));
134
- }
135
-
136
- // After install: re-decide and run appropriate actions.
137
- // RECIPE_UP_INSTALL_ATTEMPTED guards against infinite loops if yarn-setup was a no-op.
138
- if (report.decision === 'install' && !process.env['RECIPE_UP_INSTALL_ATTEMPTED']) {
139
- process.env['RECIPE_UP_INSTALL_ATTEMPTED'] = '1';
140
- const postInstall = await decideMobileReadiness(target, {
141
- watcherPort: opts.watcherPort,
142
- metroLog: opts.metroLog,
143
- platform,
144
- preflightMode,
145
- });
146
- switch (postInstall.decision) {
147
- case 'install': {
148
- // Yarn setup ran but runtime is still unresolved — teach and fail.
149
- const msg =
150
- `mobile prepare: dependencies installed but runtime is still unresolved ` +
151
- `(${postInstall.reasons.join('; ')})\n` +
152
- ` Next: inspect the checkout — node_modules may be incomplete or yarn.lock drifted.`;
153
- if (!json) process.stderr.write(`${msg}\n`);
154
- return { status: EXIT.runtime, output: msg };
155
- }
156
- case 'ready': {
157
- // Deps resolved and runtime appears ready — still confirm bridge is live.
158
- const bridge = await dispatchAction({ id: 'wait-for-bridge', cwd: target }, target, platform, json, preflightMode);
159
- if (bridge.status !== 0) return bridge;
160
- break;
161
- }
162
- case 'launch': {
163
- for (const action of postInstall.actions) {
164
- const result = await dispatchAction(action, target, platform, json, preflightMode);
165
- if (result.status !== 0) return result;
166
- }
167
- break;
168
- }
169
- default: {
170
- // 'blocked' or 'unknown' after install — surface as error.
171
- const msg =
172
- `mobile prepare: post-install state is ${postInstall.decision}: ${postInstall.reasons.join('; ')}\n` +
173
- ` Next: run mm-harness verify --adapter mobile --target <checkout>`;
174
- if (!json) process.stderr.write(`${msg}\n`);
175
- return { status: EXIT.runtime, output: msg };
176
- }
177
- }
178
- }
179
-
180
- return { status: 0, output: '' };
181
- }
182
-
183
- // Long-running leaves stream their output to stderr live (spawnScriptStreaming),
184
- // so the multi-minute prepare phase is not silent; stdout stays reserved for the
185
- // caller's --json envelope. Async so the parent can await each leaf in sequence.
186
- async function dispatchAction(
187
- action: { id: string; argv?: string[]; cwd?: string },
188
- target: string,
189
- platform: string,
190
- json: boolean,
191
- preflightMode = 'fast',
192
- ): Promise<ScriptResult> {
193
- const cwd = action.cwd ?? target;
194
- switch (action.id) {
195
- case 'yarn-setup': {
196
- // Leaf: install node_modules (deps missing or stale); `yarn setup` runs pods.
197
- const leaf = path.join(runnerDir, 'adapters/mobile/yarn-setup.sh');
198
- return spawnScriptStreaming(leaf, ['--target', cwd], target, POD_PROBE_ENV);
199
- }
200
- case 'start-metro': {
201
- // Leaf: ensure Metro is running; argv may carry --clear for cache reset.
202
- const leaf = path.join(runnerDir, 'adapters/mobile/start-metro.sh');
203
- const extra: string[] = action.argv ?? [];
204
- return spawnScriptStreaming(leaf, ['--target', cwd, ...extra], target);
205
- }
206
- case 'prewarm-bundle': {
207
- // Leaf: curl the bundle URL before opening the dev client.
208
- const leaf = path.join(runnerDir, 'adapters/mobile/prewarm-bundle.sh');
209
- return spawnScriptStreaming(leaf, ['--platform', platform, '--target', cwd], target);
210
- }
211
- case 'wait-for-bridge': {
212
- // Leaf: poll CDP bridge until a route target registers.
213
- const leaf = path.join(runnerDir, 'adapters/mobile/wait-for-bridge.sh');
214
- return spawnScriptStreaming(leaf, ['--target', cwd], target);
215
- }
216
- case 'clear-metro-cache': {
217
- // Legacy alias for start-metro --clear (kept for backward compat).
218
- const leaf = path.join(runnerDir, 'adapters/mobile/start-metro.sh');
219
- return spawnScriptStreaming(leaf, ['--target', cwd, '--clear'], target);
220
- }
221
- case 'launch-mobile-runtime': {
222
- // Leaf: open the MetaMask Mobile dev client on the simulator/device. In
223
- // auto/rebuild modes this runs the native build (`yarn start:*`), which
224
- // triggers pod install — pass the pod-probe env so it stays FORCE_COLOR-safe.
225
- const leaf = path.join(runnerDir, 'adapters/mobile/open-device.sh');
226
- return spawnScriptStreaming(
227
- leaf,
228
- ['--platform', platform, '--target', cwd, '--preflight-mode', preflightMode],
229
- target,
230
- POD_PROBE_ENV,
231
- );
232
- }
233
- case 'rebuild-native-dev-client': {
234
- // Native rebuild required — the dev client binary predates a native Nitro
235
- // module. Inform the caller; a full native build is not automated here.
236
- const msg =
237
- 'rebuild-native-dev-client required: native module mismatch detected.\n' +
238
- ' Next: yarn start:ios or yarn start:android to rebuild the native dev client, ' +
239
- 'then re-run mm-harness launch.';
240
- if (!json) process.stderr.write(`${msg}\n`);
241
- return { status: EXIT.runtime, output: msg };
242
- }
243
- default:
244
- return { status: 0, output: '' };
245
- }
246
- }