@deeeed/metamask-harness 0.5.1 → 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 (89) hide show
  1. package/CHANGELOG.md +37 -20
  2. package/adapters/core/inject.sh +1 -0
  3. package/adapters/extension/cleanup.mjs +1 -1
  4. package/adapters/extension/inject.mjs +2 -1
  5. package/adapters/extension/launch-browser.cjs +18 -4
  6. package/adapters/extension/live.sh +14 -2
  7. package/adapters/extension/sidepanel-toggle.sh +33 -12
  8. package/adapters/extension/wallet-fixture-state.cjs +38 -24
  9. package/adapters/manifest.json +13 -0
  10. package/adapters/mobile/inject.sh +1 -0
  11. package/adapters/mobile/stop-metro.sh +20 -0
  12. package/adapters/shared/cli-ux.sh +20 -2
  13. package/adapters/shared/ensure-runner-deps.sh +30 -0
  14. package/adapters/shared/reap-checkout-metros.sh +53 -0
  15. package/adapters/shared/recipe-harness-root.mjs +23 -0
  16. package/adapters/shared/resolve-farmslot-ports-core.mjs +15 -6
  17. package/bin/mm-harness +45 -4
  18. package/dist/adapters/extension/runtime.js +3 -1
  19. package/dist/adapters/mobile/provision.js +34 -1
  20. package/dist/adapters/slot-ports.js +3 -8
  21. package/dist/cli.js +32 -1463
  22. package/dist/commands/call.js +183 -0
  23. package/dist/commands/completion-candidates.js +58 -0
  24. package/dist/commands/doctor.js +101 -0
  25. package/dist/commands/ensure-ready.js +24 -0
  26. package/dist/commands/flows.js +62 -0
  27. package/dist/commands/launch/extension.js +40 -0
  28. package/dist/commands/{launch.js → launch/index.js} +15 -47
  29. package/dist/commands/launch/mobile.js +10 -0
  30. package/dist/commands/manifest.js +72 -0
  31. package/dist/commands/parse-args.js +189 -0
  32. package/dist/commands/provision.js +136 -0
  33. package/dist/commands/resolve-extension.js +23 -0
  34. package/dist/commands/run-engine.js +341 -0
  35. package/dist/commands/run.js +217 -0
  36. package/dist/commands/runtime-decision.js +58 -0
  37. package/dist/commands/runtime-health.js +25 -0
  38. package/dist/commands/runtime-launch.js +139 -0
  39. package/dist/commands/self-test.js +52 -0
  40. package/dist/commands/stop.js +52 -0
  41. package/dist/harness.js +8 -48
  42. package/dist/mm-harness-cli.js +13 -8
  43. package/docs/CLI-SPEC.md +1 -1
  44. package/docs/CODE-MAP.md +62 -0
  45. package/library/README.md +14 -0
  46. package/library/actions/extension/platform/cdp.mjs +1 -1
  47. package/library/actions/extension/wallet/ensure_unlocked.mjs +6 -0
  48. package/library/actions/mobile/wallet/ensure_unlocked.mjs +13 -1
  49. package/package.json +6 -7
  50. package/src/adapters/core/surface.ts +0 -71
  51. package/src/adapters/extension/ensure-ready.ts +0 -185
  52. package/src/adapters/extension/extension-id.ts +0 -107
  53. package/src/adapters/extension/runtime-decision.ts +0 -445
  54. package/src/adapters/extension/runtime.ts +0 -407
  55. package/src/adapters/extension/surface.ts +0 -88
  56. package/src/adapters/mobile/deps-markers.ts +0 -21
  57. package/src/adapters/mobile/prepare.ts +0 -246
  58. package/src/adapters/mobile/provision.ts +0 -594
  59. package/src/adapters/mobile/runtime-decision.ts +0 -466
  60. package/src/adapters/mobile/surface.ts +0 -71
  61. package/src/adapters/resolve-farmslot-ports.ts +0 -13
  62. package/src/adapters/slot-ports.ts +0 -158
  63. package/src/adapters/surface.ts +0 -117
  64. package/src/adapters.ts +0 -601
  65. package/src/cli-color.ts +0 -92
  66. package/src/cli-commands.ts +0 -250
  67. package/src/cli-version.ts +0 -141
  68. package/src/cli.ts +0 -2091
  69. package/src/commands/debug.ts +0 -65
  70. package/src/commands/fixtures.ts +0 -198
  71. package/src/commands/launch.ts +0 -470
  72. package/src/commands/logs.ts +0 -99
  73. package/src/commands/shared.ts +0 -235
  74. package/src/commands/update.ts +0 -316
  75. package/src/completions-cache.ts +0 -86
  76. package/src/doctor.ts +0 -215
  77. package/src/harness.ts +0 -797
  78. package/src/heal-bounds.ts +0 -198
  79. package/src/index.ts +0 -15
  80. package/src/leaf-invoke.ts +0 -28
  81. package/src/live-adapter-contract.ts +0 -274
  82. package/src/manifest.ts +0 -47
  83. package/src/mm-harness-cli.ts +0 -655
  84. package/src/paths.ts +0 -198
  85. package/src/progress.ts +0 -117
  86. package/src/recording-target.ts +0 -147
  87. package/src/run-recording.ts +0 -329
  88. package/src/runner.ts +0 -108
  89. package/src/types.ts +0 -57
@@ -1,407 +0,0 @@
1
- // runtime.ts — extension runtime prepare/health over CDP.
2
- // App/instance control.
3
- import { spawn } from 'node:child_process';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
-
7
- import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, recipeRuntimeDir, resolveLocalProtocolRoot, resolveRequiredLocalProtocolRoot, runnerDir } from '../../paths.ts';
8
-
9
- // Runtime health uses package dependencies. Launching a host-managed validation
10
- // browser remains a dev-only path because it needs pool/project scripts from a
11
- // checkout.
12
- const { CdpSession, jsonGet, sleep } = await importRecipeHarnessRuntimeCdp();
13
- const { extensionIdFromTarget } = await importRecipeHarnessRuntimeBrowserExtension();
14
-
15
- export interface ExtensionRuntimeOptions {
16
- projectRoot: string;
17
- cdpPort?: string | number;
18
- slot?: string;
19
- launchExistingDist?: boolean;
20
- validationRuntimeDir?: string;
21
- healthTimeoutMs?: number;
22
- }
23
-
24
- export interface ExtensionRuntimeLaunchResult {
25
- launched: boolean;
26
- slotId?: string;
27
- cdpPort: number;
28
- runtimeDir?: string;
29
- stdout?: string;
30
- }
31
-
32
- export interface ExtensionRuntimeHealthReport {
33
- status: 'PASS' | 'FAIL';
34
- cdpPort: number;
35
- targetUrl?: string;
36
- extensionId?: string;
37
- extensionPageTargets: number;
38
- findings: string[];
39
- details: Record<string, unknown>;
40
- }
41
-
42
- interface CdpTarget {
43
- id?: string;
44
- type?: string;
45
- title?: string;
46
- url?: string;
47
- webSocketDebuggerUrl?: string;
48
- }
49
-
50
- export async function prepareExtensionRuntime(
51
- options: ExtensionRuntimeOptions,
52
- ): Promise<{ launch: ExtensionRuntimeLaunchResult | null; health: ExtensionRuntimeHealthReport }> {
53
- const projectRoot = path.resolve(options.projectRoot);
54
- const slot = resolveExtensionSlot(projectRoot, options.slot);
55
- const cdpPort = resolveCdpPort(options.cdpPort, slot);
56
- let launch: ExtensionRuntimeLaunchResult | null = null;
57
- if (options.launchExistingDist) {
58
- if (!slot) {
59
- throw new Error(
60
- `Cannot launch host-managed Extension runtime for ${projectRoot}: no configured slot maps to this repo. ` +
61
- 'Pass --slot <slot-id> or add the checkout to pool/*.json.',
62
- );
63
- }
64
- launch = await launchHostValidationBrowser({
65
- projectRoot,
66
- cdpPort,
67
- slotId: slot.id,
68
- validationRuntimeDir:
69
- options.validationRuntimeDir ?? `${recipeRuntimeDir()}/validation-${cdpPort}`,
70
- });
71
- }
72
- const health = await assertHealthyExtensionRuntime({
73
- projectRoot,
74
- cdpPort,
75
- timeoutMs: options.healthTimeoutMs,
76
- });
77
- return { launch, health };
78
- }
79
-
80
- export async function assertHealthyExtensionRuntime(options: {
81
- projectRoot: string;
82
- cdpPort: number;
83
- timeoutMs?: number;
84
- }): Promise<ExtensionRuntimeHealthReport> {
85
- const timeoutMs = options.timeoutMs ?? 30_000;
86
- const deadline = Date.now() + timeoutMs;
87
- let lastReport: ExtensionRuntimeHealthReport | null = null;
88
- while (Date.now() <= deadline) {
89
- lastReport = await checkExtensionRuntimeHealth(options.projectRoot, options.cdpPort);
90
- if (lastReport.status === 'PASS') return lastReport;
91
- await sleep(500);
92
- }
93
- const report = lastReport ?? failReport(options.cdpPort, ['CDP runtime was not probed.'], {});
94
- throw new Error(formatHealthFailure(report, options.projectRoot));
95
- }
96
-
97
- export async function checkExtensionRuntimeHealth(
98
- projectRoot: string,
99
- cdpPort: number,
100
- ): Promise<ExtensionRuntimeHealthReport> {
101
- const findings: string[] = [];
102
- let targets: CdpTarget[] = [];
103
- try {
104
- await jsonGet(`http://127.0.0.1:${cdpPort}/json/version`);
105
- const rawTargets = await jsonGet(`http://127.0.0.1:${cdpPort}/json/list`);
106
- targets = Array.isArray(rawTargets) ? rawTargets as CdpTarget[] : [];
107
- } catch (error) {
108
- return failReport(cdpPort, [`CDP is not reachable on port ${cdpPort}: ${messageOf(error)}`], {});
109
- }
110
-
111
- const extensionTargets = targets.filter((target) =>
112
- target.type === 'page' &&
113
- String(target.url ?? '').startsWith('chrome-extension://') &&
114
- String(target.url ?? '').includes('/home.html') &&
115
- Boolean(target.webSocketDebuggerUrl),
116
- );
117
- if (extensionTargets.length !== 1) {
118
- findings.push(`Expected exactly one MetaMask extension home page target, found ${extensionTargets.length}.`);
119
- }
120
- const target = extensionTargets[0];
121
- if (!target?.webSocketDebuggerUrl) {
122
- return failReport(cdpPort, findings, {
123
- targetUrls: targets.map((entry) => entry.url).filter(Boolean),
124
- });
125
- }
126
- if (String(target.url ?? '').startsWith('chrome-error://')) {
127
- findings.push(`Extension target resolved to chrome-error page: ${target.url}`);
128
- }
129
-
130
- const session = await CdpSession.connect(target.webSocketDebuggerUrl);
131
- try {
132
- await session.call('Runtime.enable');
133
- await session.call('Page.enable');
134
- const runtime = await evaluateHealth(session);
135
- if (runtime.href && !String(runtime.href).startsWith('chrome-extension://')) {
136
- findings.push(`Extension page href is not an extension URL: ${runtime.href}`);
137
- }
138
- if (runtime.backgroundUnresponsive === true) {
139
- findings.push('Extension UI reports background connection unresponsive.');
140
- }
141
- if (runtime.hasSubmitRequest !== true) {
142
- findings.push('stateHooks.submitRequestToBackground is unavailable.');
143
- }
144
- if (runtime.hasStore !== true) {
145
- findings.push('stateHooks.store is unavailable.');
146
- }
147
- if (runtime.hasPerpsStreamManager !== true) {
148
- findings.push('stateHooks.getPerpsStreamManager is unavailable.');
149
- }
150
- if (runtime.backgroundProbeOk !== true) {
151
- findings.push(`Perps background read probe failed: ${runtime.backgroundProbeError ?? 'unknown error'}.`);
152
- }
153
- const extensionId = safeExtensionId(target);
154
- return {
155
- status: findings.length === 0 ? 'PASS' : 'FAIL',
156
- cdpPort,
157
- targetUrl: target.url,
158
- extensionId,
159
- extensionPageTargets: extensionTargets.length,
160
- findings,
161
- details: {
162
- projectRoot,
163
- targetUrls: targets.map((entry) => entry.url).filter(Boolean),
164
- runtime,
165
- },
166
- };
167
- } finally {
168
- session.close();
169
- }
170
- }
171
-
172
- function failReport(
173
- cdpPort: number,
174
- findings: string[],
175
- details: Record<string, unknown>,
176
- ): ExtensionRuntimeHealthReport {
177
- return {
178
- status: 'FAIL',
179
- cdpPort,
180
- extensionPageTargets: 0,
181
- findings,
182
- details,
183
- };
184
- }
185
-
186
- async function evaluateHealth(session: any): Promise<Record<string, unknown>> {
187
- const result = await session.call('Runtime.evaluate', {
188
- expression: `(() => {
189
- const hooks = globalThis.stateHooks || {};
190
- const bodyText = document.body?.innerText || '';
191
- const storeState = hooks.store?.getState?.() || {};
192
- const manager = hooks.getPerpsStreamManager?.();
193
- const accountCache = manager?.account?.cache;
194
- return Promise.race([
195
- (async () => {
196
- let backgroundProbeOk = false;
197
- let backgroundProbeError = null;
198
- if (typeof hooks.submitRequestToBackground === 'function') {
199
- try {
200
- const accountState = await hooks.submitRequestToBackground('perpsGetAccountState', []);
201
- backgroundProbeOk = Boolean(accountState && typeof accountState === 'object');
202
- if (!backgroundProbeOk) backgroundProbeError = 'perpsGetAccountState returned an empty result';
203
- } catch (error) {
204
- backgroundProbeError = String(error?.message || error);
205
- }
206
- } else {
207
- backgroundProbeError = 'submitRequestToBackground is not a function';
208
- }
209
- return {
210
- href: location.href,
211
- title: document.title,
212
- hookKeys: Object.keys(hooks),
213
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
214
- hasStore: Boolean(hooks.store),
215
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
216
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
217
- activeProvider: storeState.metamask?.activeProvider || null,
218
- isTestnet: Boolean(storeState.metamask?.isTestnet),
219
- perpsManagerInitialized: Boolean(manager?.isInitialized?.()),
220
- positionsCacheIsArray: Array.isArray(manager?.positions?.cache),
221
- ordersCacheIsArray: Array.isArray(manager?.orders?.cache),
222
- accountCachePresent: Boolean(accountCache && typeof accountCache === 'object'),
223
- backgroundProbeOk,
224
- backgroundProbeError,
225
- };
226
- })(),
227
- new Promise((resolve) => setTimeout(() => resolve({
228
- href: location.href,
229
- title: document.title,
230
- hookKeys: Object.keys(hooks),
231
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
232
- hasStore: Boolean(hooks.store),
233
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
234
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
235
- backgroundProbeOk: false,
236
- backgroundProbeError: 'perpsGetAccountState timed out after 5000ms',
237
- }), 5000)),
238
- ]);
239
- })()`,
240
- awaitPromise: true,
241
- returnByValue: true,
242
- });
243
- if (result.exceptionDetails) {
244
- throw new Error(
245
- result.exceptionDetails.exception?.description ??
246
- result.exceptionDetails.text ??
247
- 'Extension runtime health evaluation failed.',
248
- );
249
- }
250
- return result.result?.value ?? {};
251
- }
252
-
253
- export function formatHealthFailure(report: ExtensionRuntimeHealthReport, projectRoot: string): string {
254
- return [
255
- `Extension runtime health check failed for ${projectRoot} on CDP port ${report.cdpPort}.`,
256
- ...report.findings.map((finding) => `- ${finding}`),
257
- 'Use the host-managed validation launcher, for example:',
258
- ` mm-harness run <recipe.json> --adapter extension --target ${projectRoot} --slot <slot-id> --cdp-port ${report.cdpPort} --launch-existing-dist --artifacts-dir <artifacts-dir>`,
259
- `Health details: ${JSON.stringify(report.details)}`,
260
- ].join('\n');
261
- }
262
-
263
- function resolveCdpPort(rawPort: string | number | undefined, slot: ResolvedSlot | null): number {
264
- const raw = rawPort ?? process.env.CDP_PORT ?? process.env.RECIPE_CDP_PORT ?? slot?.cdpPort;
265
- const port = Number(raw);
266
- if (!Number.isInteger(port) || port <= 0) {
267
- throw new Error('Extension runtime requires --cdp-port, CDP_PORT, RECIPE_CDP_PORT, or a configured slot with resources.browser.cdp_port.');
268
- }
269
- return port;
270
- }
271
-
272
- interface ResolvedSlot {
273
- id: string;
274
- repo: string;
275
- cdpPort?: number;
276
- }
277
-
278
- function resolveExtensionSlot(projectRoot: string, requestedSlot?: string): ResolvedSlot | null {
279
- const protocolRoot = resolveLocalProtocolRoot();
280
- if (!protocolRoot) {
281
- if (requestedSlot) throw new Error('Extension slot lookup requires a local host checkout when --slot is provided.');
282
- return null;
283
- }
284
- const poolsDir = path.join(protocolRoot, 'pool');
285
- if (!fs.existsSync(poolsDir)) return null;
286
- for (const file of fs.readdirSync(poolsDir)) {
287
- // Only canonical pool files: skip backups (`*.json.bak.*`) and any non-.json,
288
- // so a stale backup can't resolve a slot the live pool no longer defines.
289
- if (!file.endsWith('.json') || file.includes('.bak')) continue;
290
- const pool = JSON.parse(fs.readFileSync(path.join(poolsDir, file), 'utf8')) as { slots?: unknown[] };
291
- for (const rawSlot of pool.slots ?? []) {
292
- if (!rawSlot || typeof rawSlot !== 'object') continue;
293
- const slot = rawSlot as Record<string, any>;
294
- if (requestedSlot && slot.id !== requestedSlot) continue;
295
- if (!requestedSlot) {
296
- // Auto-match by repo requires an ABSOLUTE repo path. A relative entry —
297
- // e.g. demo pools' "repo": "." — resolves to the runner's CWD and would
298
- // spuriously shadow the real slot for whatever repo it runs in.
299
- const repo = String(slot.repo ?? '');
300
- if (!path.isAbsolute(repo) || path.resolve(repo) !== projectRoot) continue;
301
- }
302
- return {
303
- id: String(slot.id),
304
- repo: path.resolve(String(slot.repo)),
305
- cdpPort: Number(slot.resources?.browser?.cdp_port || slot.resources?.browser?.cdpPort || undefined) || undefined,
306
- };
307
- }
308
- }
309
- return null;
310
- }
311
-
312
- async function launchHostValidationBrowser(options: {
313
- projectRoot: string;
314
- cdpPort: number;
315
- slotId: string;
316
- validationRuntimeDir: string;
317
- }): Promise<ExtensionRuntimeLaunchResult> {
318
- const protocolRoot = resolveRequiredLocalProtocolRoot('Extension validation browser launch');
319
- const script = path.join(protocolRoot, 'projects/metamask-extension-farm/setup/launch-validation-browser.sh');
320
- if (!fs.existsSync(script)) {
321
- throw new Error(`Extension validation launcher not found: ${script}`);
322
- }
323
- await killCdpPort(options.cdpPort);
324
- const result = await runProcess('bash', [
325
- script,
326
- options.slotId,
327
- '--cdp-port',
328
- String(options.cdpPort),
329
- '--runtime-dir',
330
- options.validationRuntimeDir,
331
- '--no-landing',
332
- ], {
333
- cwd: protocolRoot,
334
- env: process.env,
335
- timeoutMs: 180_000,
336
- });
337
- if (result.timedOut) {
338
- throw new Error(`Extension validation launcher timed out after 180000ms.\n${result.stdout}\n${result.stderr}`);
339
- }
340
- if (result.exitCode !== 0) {
341
- throw new Error(`Extension validation launcher failed with exit ${result.exitCode}.\n${result.stdout}\n${result.stderr}`);
342
- }
343
- return {
344
- launched: true,
345
- slotId: options.slotId,
346
- cdpPort: options.cdpPort,
347
- runtimeDir: options.validationRuntimeDir,
348
- stdout: result.stdout,
349
- };
350
- }
351
-
352
- function runProcess(command: string, args: string[], options: { cwd: string; env: NodeJS.ProcessEnv; timeoutMs?: number }): Promise<{ exitCode: number | null; stdout: string; stderr: string; timedOut: boolean }> {
353
- return new Promise((resolve, reject) => {
354
- const child = spawn(command, args, { cwd: options.cwd, env: options.env, stdio: ['ignore', 'pipe', 'pipe'] });
355
- let stdout = '';
356
- let stderr = '';
357
- let settled = false;
358
- const timeout = options.timeoutMs
359
- ? setTimeout(() => {
360
- if (settled) return;
361
- settled = true;
362
- child.kill('SIGTERM');
363
- setTimeout(() => {
364
- if (!child.killed) child.kill('SIGKILL');
365
- }, 1000);
366
- resolve({ exitCode: null, stdout, stderr, timedOut: true });
367
- }, options.timeoutMs)
368
- : undefined;
369
- child.stdout.on('data', (chunk) => { stdout += chunk; });
370
- child.stderr.on('data', (chunk) => { stderr += chunk; });
371
- child.on('error', (error) => {
372
- if (settled) return;
373
- settled = true;
374
- if (timeout) clearTimeout(timeout);
375
- reject(error);
376
- });
377
- child.on('close', (exitCode) => {
378
- if (settled) return;
379
- settled = true;
380
- if (timeout) clearTimeout(timeout);
381
- resolve({ exitCode, stdout, stderr, timedOut: false });
382
- });
383
- });
384
- }
385
-
386
- async function killCdpPort(cdpPort: number): Promise<void> {
387
- if (process.platform !== 'darwin' && process.platform !== 'linux') return;
388
- await runProcess('bash', ['-lc', `lsof -ti tcp:${cdpPort} -sTCP:LISTEN | xargs -r kill -TERM || true; sleep 1; lsof -ti tcp:${cdpPort} -sTCP:LISTEN | xargs -r kill -KILL || true`], {
389
- cwd: runnerDir,
390
- env: process.env,
391
- timeoutMs: 10_000,
392
- });
393
- }
394
-
395
- function safeExtensionId(target: CdpTarget): string | undefined {
396
- try {
397
- return extensionIdFromTarget(target);
398
- } catch (_error) {
399
- // Non-extension CDP targets are expected while scanning all browser tabs.
400
- // Treat them as unmatched targets instead of failing runtime discovery.
401
- return undefined;
402
- }
403
- }
404
-
405
- function messageOf(error: unknown): string {
406
- return error instanceof Error ? error.message : String(error);
407
- }
@@ -1,88 +0,0 @@
1
- // Extension surface: delegates to the existing extension readiness/port plumbing
2
- // and the webpack watcher stop.
3
- import { spawnSync } from 'node:child_process';
4
- import path from 'node:path';
5
-
6
- import { recipeRuntimePath } from '../../paths.ts';
7
- import { resolveExtensionSlotPorts, stopExtensionWatcher } from '../slot-ports.ts';
8
- import { decideExtensionReadiness } from './runtime-decision.ts';
9
- import type {
10
- AdapterDevServerStop,
11
- AdapterLogSource,
12
- AdapterRuntimeStatus,
13
- AdapterRunwayProvisionResult,
14
- AdapterSurface,
15
- } from '../surface.ts';
16
-
17
- // Map the webpack watch-log health onto a dev-server up/down/building/errors
18
- // status doctor renders the same way as mobile's Metro line.
19
- function watcherStatus(buildLog: string): string {
20
- if (buildLog === 'ok') return 'up';
21
- if (buildLog === 'no-watch') return 'down';
22
- return buildLog; // 'building' | 'errors'
23
- }
24
-
25
- export const extensionSurface: AdapterSurface = {
26
- adapter: 'extension',
27
- headless: false,
28
-
29
- resolveSlotPorts(target: string): void {
30
- resolveExtensionSlotPorts(target);
31
- },
32
-
33
- async runtimeStatus(target: string): Promise<AdapterRuntimeStatus> {
34
- const cdpPort = process.env.CDP_PORT ? parseInt(process.env.CDP_PORT, 10) : undefined;
35
- const report = await decideExtensionReadiness(target, { cdpPort });
36
- return {
37
- decision: report.decision,
38
- reasonCode: report.reasonCode,
39
- reasons: report.reasons,
40
- deps: report.checks.deps.status,
41
- devServer: { label: 'webpack', status: watcherStatus(report.checks.buildLog.status) },
42
- };
43
- },
44
-
45
- runwayProvision: {
46
- async run(target, options): Promise<AdapterRunwayProvisionResult> {
47
- return {
48
- schemaVersion: 1,
49
- command: 'provision',
50
- adapter: 'extension',
51
- target: path.resolve(target),
52
- status: 'fail',
53
- exitCode: 2,
54
- error: {
55
- code: 'UNSUPPORTED_ADAPTER',
56
- message: 'extension uses a browser runtime; Runway mobile provisioning is not supported.',
57
- userAction: options.rerunCommand || 'mm-harness provision --adapter extension',
58
- },
59
- };
60
- },
61
- },
62
-
63
- devServer: {
64
- describe: () => 'webpack watcher',
65
- stop(target: string): AdapterDevServerStop {
66
- const signalled = stopExtensionWatcher(target);
67
- const port = process.env.WATCHER_PORT ?? 'default';
68
- spawnSync('tmux', ['kill-window', '-t', `webpack-${port}`], { stdio: 'ignore', timeout: 2000 });
69
- const summary = signalled > 0
70
- ? `stopped webpack watcher (${signalled} process${signalled === 1 ? '' : 'es'}) for ${target}`
71
- : `webpack watcher not running for ${target} — nothing to stop`;
72
- return { kind: 'stopped', status: 0, summary, signalled };
73
- },
74
- },
75
-
76
- logSources(target: string): AdapterLogSource[] {
77
- return [
78
- { label: 'webpack', path: recipeRuntimePath(target, 'webpack.log') },
79
- { label: 'watcher', path: recipeRuntimePath(target, 'recipe-harness-webpack.log') },
80
- { label: 'rebuild', path: recipeRuntimePath(target, 'rebuild.log') },
81
- ];
82
- },
83
-
84
- hints: {
85
- launch: 'mm-harness launch',
86
- relaunch: 'mm-harness launch --build',
87
- },
88
- };
@@ -1,21 +0,0 @@
1
- /** MetaMask Mobile product markers for deps-readiness partial checks. */
2
- export const MOBILE_PRODUCT_MARKERS = [
3
- 'node_modules/.yarn-state.yml',
4
- 'app/core/InpageBridgeWeb3.js',
5
- 'docs/assets/termsOfUse.html',
6
- 'app/util/termsOfUse/termsOfUseContent.ts',
7
- 'node_modules/.bin/anvil',
8
- ];
9
-
10
- export const MOBILE_IOS_NATIVE_MARKERS = ['ios/Podfile.lock'];
11
- export const MOBILE_ANDROID_NATIVE_MARKERS = ['android/gradle.properties'];
12
-
13
- export function mobileProductMarkers(platform?: string): string[] {
14
- const markers = [...MOBILE_PRODUCT_MARKERS];
15
- const resolved = (platform ?? process.env.PLATFORM ?? process.env.RECIPE_HARNESS_PLATFORM ?? '')
16
- .trim()
17
- .toLowerCase();
18
- if (resolved === 'ios') markers.push(...MOBILE_IOS_NATIVE_MARKERS);
19
- else if (resolved === 'android') markers.push(...MOBILE_ANDROID_NATIVE_MARKERS);
20
- return markers;
21
- }