@deeeed/metamask-harness 0.5.1 → 0.6.1

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 (90) hide show
  1. package/CHANGELOG.md +47 -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/start-metro.sh +6 -0
  12. package/adapters/mobile/stop-metro.sh +20 -0
  13. package/adapters/shared/cli-ux.sh +20 -2
  14. package/adapters/shared/ensure-runner-deps.sh +30 -0
  15. package/adapters/shared/reap-checkout-metros.sh +58 -0
  16. package/adapters/shared/recipe-harness-root.mjs +23 -0
  17. package/adapters/shared/resolve-farmslot-ports-core.mjs +15 -6
  18. package/bin/mm-harness +45 -4
  19. package/dist/adapters/extension/runtime.js +3 -1
  20. package/dist/adapters/mobile/provision.js +34 -1
  21. package/dist/adapters/slot-ports.js +3 -8
  22. package/dist/cli.js +32 -1463
  23. package/dist/commands/call.js +183 -0
  24. package/dist/commands/completion-candidates.js +58 -0
  25. package/dist/commands/doctor.js +101 -0
  26. package/dist/commands/ensure-ready.js +24 -0
  27. package/dist/commands/flows.js +62 -0
  28. package/dist/commands/launch/extension.js +40 -0
  29. package/dist/commands/{launch.js → launch/index.js} +15 -47
  30. package/dist/commands/launch/mobile.js +10 -0
  31. package/dist/commands/manifest.js +72 -0
  32. package/dist/commands/parse-args.js +189 -0
  33. package/dist/commands/provision.js +136 -0
  34. package/dist/commands/resolve-extension.js +23 -0
  35. package/dist/commands/run-engine.js +341 -0
  36. package/dist/commands/run.js +217 -0
  37. package/dist/commands/runtime-decision.js +58 -0
  38. package/dist/commands/runtime-health.js +25 -0
  39. package/dist/commands/runtime-launch.js +139 -0
  40. package/dist/commands/self-test.js +52 -0
  41. package/dist/commands/stop.js +52 -0
  42. package/dist/harness.js +8 -48
  43. package/dist/mm-harness-cli.js +13 -8
  44. package/docs/CLI-SPEC.md +1 -1
  45. package/docs/CODE-MAP.md +62 -0
  46. package/library/README.md +14 -0
  47. package/library/actions/extension/platform/cdp.mjs +1 -1
  48. package/library/actions/extension/wallet/ensure_unlocked.mjs +6 -0
  49. package/library/actions/mobile/wallet/ensure_unlocked.mjs +13 -1
  50. package/package.json +6 -7
  51. package/src/adapters/core/surface.ts +0 -71
  52. package/src/adapters/extension/ensure-ready.ts +0 -185
  53. package/src/adapters/extension/extension-id.ts +0 -107
  54. package/src/adapters/extension/runtime-decision.ts +0 -445
  55. package/src/adapters/extension/runtime.ts +0 -407
  56. package/src/adapters/extension/surface.ts +0 -88
  57. package/src/adapters/mobile/deps-markers.ts +0 -21
  58. package/src/adapters/mobile/prepare.ts +0 -246
  59. package/src/adapters/mobile/provision.ts +0 -594
  60. package/src/adapters/mobile/runtime-decision.ts +0 -466
  61. package/src/adapters/mobile/surface.ts +0 -71
  62. package/src/adapters/resolve-farmslot-ports.ts +0 -13
  63. package/src/adapters/slot-ports.ts +0 -158
  64. package/src/adapters/surface.ts +0 -117
  65. package/src/adapters.ts +0 -601
  66. package/src/cli-color.ts +0 -92
  67. package/src/cli-commands.ts +0 -250
  68. package/src/cli-version.ts +0 -141
  69. package/src/cli.ts +0 -2091
  70. package/src/commands/debug.ts +0 -65
  71. package/src/commands/fixtures.ts +0 -198
  72. package/src/commands/launch.ts +0 -470
  73. package/src/commands/logs.ts +0 -99
  74. package/src/commands/shared.ts +0 -235
  75. package/src/commands/update.ts +0 -316
  76. package/src/completions-cache.ts +0 -86
  77. package/src/doctor.ts +0 -215
  78. package/src/harness.ts +0 -797
  79. package/src/heal-bounds.ts +0 -198
  80. package/src/index.ts +0 -15
  81. package/src/leaf-invoke.ts +0 -28
  82. package/src/live-adapter-contract.ts +0 -274
  83. package/src/manifest.ts +0 -47
  84. package/src/mm-harness-cli.ts +0 -655
  85. package/src/paths.ts +0 -198
  86. package/src/progress.ts +0 -117
  87. package/src/recording-target.ts +0 -147
  88. package/src/run-recording.ts +0 -329
  89. package/src/runner.ts +0 -108
  90. 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
- }