@deeeed/metamask-harness 0.79.0 → 0.80.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 +47 -0
- package/README.md +1 -1
- package/adapters/extension/ensure-browser.sh +2 -2
- package/adapters/extension/inject.mjs +0 -1
- package/adapters/extension/launch-browser.cjs +39 -613
- package/adapters/extension/readiness.mjs +2 -5
- package/adapters/extension/reattach.sh +2 -2
- package/adapters/extension/stamp-runtime-title.cjs +1 -1
- package/adapters/manifest.json +2 -34
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
- package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
- package/adapters/mobile/inject.sh +16 -0
- package/adapters/mobile/launch-console-forwarder.cjs +1 -2
- package/adapters/mobile/open-device.sh +2 -2
- package/adapters/mobile/reload-app.mjs +4 -4
- package/adapters/mobile/reset-app-data.sh +1 -1
- package/adapters/mobile/start-console-forwarder.sh +3 -2
- package/adapters/mobile/start-metro.sh +17 -12
- package/adapters/mobile/wait-for-bridge.cjs +2 -2
- package/adapters/shared/harness-source-fingerprint.mjs +13 -8
- package/adapters/shared/open-debug.mjs +2 -2
- package/adapters/shared/sync-wallet-fixture.sh +8 -5
- package/adapters/terminal/cleanup.mjs +2 -1
- package/adapters/terminal/inject.mjs +2 -1
- package/bin/mm-harness +9 -28
- package/dist/adapters/checkout-files.js +16 -0
- package/dist/adapters/console-capture.js +14 -1
- package/dist/adapters/core/actions.js +4 -3
- package/dist/adapters/core/overlay.js +1 -1
- package/dist/adapters/core/readiness.js +28 -1
- package/dist/adapters/core/run.js +2 -2
- package/dist/adapters/core/surface.js +16 -1
- package/dist/adapters/extension/browser-pid.js +56 -0
- package/dist/adapters/extension/console-capture.js +1 -1
- package/dist/adapters/extension/doctor-checks.js +1 -1
- package/dist/adapters/extension/ensure-ready.js +5 -4
- package/dist/adapters/extension/extension-id.js +1 -1
- package/dist/adapters/extension/feature-flags.js +1 -1
- package/dist/adapters/extension/fixtures.js +1 -2
- package/dist/adapters/extension/launch.js +4 -5
- package/dist/adapters/extension/launched-browser.js +1 -2
- package/dist/adapters/extension/network-observer.js +1 -1
- package/dist/adapters/extension/overlay.js +1 -1
- package/dist/adapters/extension/product-config.js +1 -1
- package/dist/adapters/extension/readiness.js +1 -1
- package/dist/adapters/extension/reload.js +1 -1
- package/dist/adapters/extension/run.js +24 -10
- package/dist/adapters/extension/runtime-decision.js +2 -1
- package/dist/adapters/extension/runtime.js +9 -5
- package/dist/adapters/extension/surface.js +15 -2
- package/dist/adapters/extension/verify.js +2 -2
- package/dist/adapters/harness-freshness.js +2 -1
- package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
- package/dist/adapters/mobile/app-identity.js +10 -0
- package/dist/adapters/mobile/doctor-checks.js +1 -1
- package/dist/adapters/mobile/fixtures.js +1 -1
- package/dist/adapters/mobile/launch.js +27 -28
- package/dist/adapters/mobile/lifecycle.js +1 -1
- package/dist/adapters/mobile/metro-env.js +9 -59
- package/dist/adapters/mobile/performance-observer.js +1 -1
- package/dist/adapters/mobile/prepare-target.js +1 -1
- package/dist/adapters/mobile/prepare.js +3 -4
- package/dist/adapters/mobile/provision.js +1 -1
- package/dist/adapters/mobile/readiness.js +1 -1
- package/dist/adapters/mobile/recording-target.js +15 -0
- package/dist/adapters/mobile/release-artifact-state.js +1 -1
- package/dist/adapters/mobile/reload.js +1 -1
- package/dist/adapters/mobile/run.js +8 -7
- package/dist/adapters/mobile/runtime-decision.js +2 -4
- package/dist/adapters/mobile/source-freshness.js +22 -126
- package/dist/adapters/mobile/surface.js +26 -3
- package/dist/adapters/mobile/ui.js +16 -5
- package/dist/adapters/mobile/verify.js +2 -3
- package/dist/adapters/performance/cdp-trace.js +1 -1
- package/dist/adapters/performance/device-platform.js +1 -1
- package/dist/adapters/slot-ports.js +1 -1
- package/dist/adapters/surface.js +1 -0
- package/dist/adapters/terminal/console-capture.js +1 -1
- package/dist/adapters/terminal/launch.js +1 -1
- package/dist/adapters/terminal/run.js +25 -2
- package/dist/adapters/terminal/surface.js +7 -1
- package/dist/adapters.js +8 -4
- package/dist/advisor-policy.js +1 -1
- package/dist/checkout-view.js +1 -2
- package/dist/cli.js +28 -15
- package/dist/command-contract.js +1 -26
- package/dist/commands/call.js +10 -24
- package/dist/commands/check.js +3 -4
- package/dist/commands/checklist.js +1 -1
- package/dist/commands/completion-candidates.js +2 -10
- package/dist/commands/config.js +2 -2
- package/dist/commands/core-readiness.js +12 -7
- package/dist/commands/debug.js +3 -3
- package/dist/commands/device-target.js +5 -5
- package/dist/commands/doctor.js +2 -14
- package/dist/commands/domain.js +2 -2
- package/dist/commands/ensure-ready.js +1 -7
- package/dist/commands/fixtures.js +5 -6
- package/dist/commands/help.js +3 -4
- package/dist/commands/launch/device.js +10 -17
- package/dist/commands/launch/extension.js +2 -8
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/commands/list-executables.js +1 -3
- package/dist/commands/logs.js +3 -3
- package/dist/commands/manifest.js +1 -12
- package/dist/commands/mobile-device-view.js +4 -2
- package/dist/commands/overlay.js +110 -0
- package/dist/commands/pin.js +1 -1
- package/dist/commands/pr-body.js +2 -2
- package/dist/commands/prepare.js +4 -9
- package/dist/commands/provision.js +3 -12
- package/dist/commands/recipe-advice.js +1 -2
- package/dist/commands/recipe-quality.js +2 -3
- package/dist/commands/resolve-extension.js +1 -7
- package/dist/commands/review.js +4 -4
- package/dist/commands/run-engine.js +12 -51
- package/dist/commands/run.js +4 -25
- package/dist/commands/runtime-decision.js +1 -6
- package/dist/commands/runtime-health.js +1 -7
- package/dist/commands/runtime-launch.js +1 -13
- package/dist/commands/self-test.js +2 -6
- package/dist/commands/status-watch.js +1 -2
- package/dist/commands/status.js +2 -5
- package/dist/commands/task-init.js +2 -3
- package/dist/commands/tutorial.js +2 -2
- package/dist/commands/update.js +1 -1
- package/dist/completions-cache.js +1 -1
- package/dist/console-classifier.js +19 -0
- package/dist/doctor.js +2 -3
- package/dist/funding-execution-context.js +15 -1
- package/dist/host.js +32 -0
- package/dist/index.js +1 -6
- package/dist/live-tsx.js +49 -0
- package/dist/mm-harness-cli.js +2 -4
- package/dist/network-observation.js +4 -4
- package/dist/paths.js +34 -64
- package/dist/performance-observation.js +2 -2
- package/dist/progress.js +1 -1
- package/dist/recipe-assistance-context.js +1 -3
- package/dist/recipe-security.js +2 -59
- package/dist/review/knowledge.js +2 -2
- package/dist/runner.js +5 -3
- package/dist/runtime-context.js +1 -1
- package/dist/task-view.js +2 -3
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/cli-contract.md +2 -2
- package/docs/feature-guard.md +90 -0
- package/library/README.md +3 -1
- package/library/actions/core/perps/_controller.mjs +153 -130
- package/library/actions/core/perps/quote_fees.mjs +58 -0
- package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
- package/library/actions/core/wallet/list_accounts.mjs +14 -57
- package/library/actions/extension/feature_flags/state.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +2 -8
- package/library/actions/harness-exports.mjs +1 -6
- package/library/actions/mobile/app/network-control.mjs +6 -8
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +8 -8
- package/library/actions/mobile/platform/native-session-name.mjs +21 -1
- package/library/actions/mobile/platform/native-session.mjs +3 -4
- package/library/actions/mobile/platform/observe-ui.mjs +12 -6
- package/library/actions/mobile/ui/native-navigation.mjs +4 -3
- package/library/actions/mobile/wallet/native-ui.mjs +3 -2
- package/library/actions/shared/analytics/_adapter.mjs +1 -1
- package/library/actions/shared/console-findings.mjs +1 -1
- package/library/actions/terminal/platform/runtime.mjs +2 -1
- package/library/manifests/core.action-manifest.json +83 -13
- package/package.json +8 -9
- package/adapters/extension/lib/slot-title.cjs +0 -292
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
- package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
- package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
- package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
- package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
- package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
- package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
- package/adapters/mobile/coalesce-metro-log.cjs +0 -24
- package/adapters/mobile/launch-metro.cjs +0 -77
- package/adapters/mobile/metro-config.cjs +0 -98
- package/adapters/mobile/metro-log-generation.cjs +0 -106
- package/dist/adapters/mobile/frame-metrics.js +0 -118
- package/dist/adapters/mobile/video-recorder.js +0 -289
- package/dist/artifact-files.js +0 -92
- package/dist/checkout-lock.js +0 -149
- package/dist/cli-color.js +0 -98
- package/dist/command-journal.js +0 -308
- package/dist/commands/last.js +0 -60
- package/dist/commands/launch/index.js +0 -109
- package/dist/commands/parse-args.js +0 -279
- package/dist/commands/reload.js +0 -41
- package/dist/commands/run-report.js +0 -241
- package/dist/commands/shared.js +0 -323
- package/dist/commands/stop.js +0 -60
- package/dist/devices.js +0 -100
- package/dist/execution-provenance.js +0 -332
- package/dist/harness.js +0 -451
- package/dist/heal-bounds.js +0 -137
- package/dist/json-stream.js +0 -60
- package/dist/leaf-invoke.js +0 -19
- package/dist/library-provenance.js +0 -33
- package/dist/live-adapter-contract.js +0 -380
- package/dist/recording-target.js +0 -120
- package/dist/run-diagnostics.js +0 -364
- package/dist/run-recording.js +0 -481
- package/dist/runtime-proof.js +0 -90
- package/library/actions/mobile/platform/tool-paths.mjs +0 -123
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// The checkout's wallet fixture expanded into accounts: a mnemonic entry with
|
|
2
|
+
// count N yields N accounts at address indexes 0..N-1, named dev1..devN from the
|
|
3
|
+
// entry's name. list_accounts shows them; Core Perps actions select from them.
|
|
4
|
+
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
7
|
+
|
|
8
|
+
import { walletFixturePath } from '../../harness-exports.mjs';
|
|
9
|
+
|
|
10
|
+
function fixtureAccountName(base, index, count) {
|
|
11
|
+
if (count === 1) return base || 'account1';
|
|
12
|
+
const match = /^(.*?)(\d+)$/u.exec(base || 'account1');
|
|
13
|
+
return match
|
|
14
|
+
? `${match[1]}${Number(match[2]) + index}`
|
|
15
|
+
: `${base || 'account'}${index + 1}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function mnemonicCount(entry, fixturePath, index) {
|
|
19
|
+
const count = Number(entry.count ?? entry.numberOfAccounts ?? 1);
|
|
20
|
+
if (!Number.isInteger(count) || count < 1 || count > 100) {
|
|
21
|
+
throw new Error(`${fixturePath} accounts[${index}] mnemonic count must be an integer from 1 through 100.`);
|
|
22
|
+
}
|
|
23
|
+
return count;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Expand fixture entries into `{ name, address, signer }`. `signer` is the viem
|
|
28
|
+
* account holding the key: use it to sign, never serialize it.
|
|
29
|
+
*/
|
|
30
|
+
export function deriveFixtureAccounts(fixture, fixturePath) {
|
|
31
|
+
if (!Array.isArray(fixture.accounts) || fixture.accounts.length === 0) {
|
|
32
|
+
throw new Error(`wallet fixture at ${fixturePath} has no accounts array.`);
|
|
33
|
+
}
|
|
34
|
+
return fixture.accounts.flatMap((entry, fixtureIndex) => {
|
|
35
|
+
if (!entry || typeof entry !== 'object' || typeof entry.value !== 'string' || !entry.value.trim()) {
|
|
36
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}] must contain a non-empty value.`);
|
|
37
|
+
}
|
|
38
|
+
if (entry.type === 'mnemonic') {
|
|
39
|
+
const count = mnemonicCount(entry, fixturePath, fixtureIndex);
|
|
40
|
+
return Array.from({ length: count }, (_, addressIndex) => {
|
|
41
|
+
const signer = mnemonicToAccount(entry.value.trim(), { addressIndex });
|
|
42
|
+
return { name: fixtureAccountName(entry.name, addressIndex, count), address: signer.address, signer };
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (entry.type === 'privateKey') {
|
|
46
|
+
const raw = entry.value.trim();
|
|
47
|
+
const normalized = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
48
|
+
if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
|
|
49
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}] privateKey is not a 32-byte hex key.`);
|
|
50
|
+
}
|
|
51
|
+
const signer = privateKeyToAccount(normalized);
|
|
52
|
+
return [{ name: entry.name || `account${fixtureIndex + 1}`, address: signer.address, signer }];
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}].type must be mnemonic or privateKey.`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The checkout's fixture accounts, or null when it has no wallet fixture. */
|
|
59
|
+
export async function readFixtureAccounts(projectRoot) {
|
|
60
|
+
const fixturePath = walletFixturePath(projectRoot);
|
|
61
|
+
let raw;
|
|
62
|
+
try {
|
|
63
|
+
raw = await readFile(fixturePath, 'utf8');
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error?.code === 'ENOENT') return null;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
return { fixturePath, accounts: deriveFixtureAccounts(JSON.parse(raw), fixturePath) };
|
|
69
|
+
}
|
|
@@ -3,72 +3,29 @@
|
|
|
3
3
|
|
|
4
4
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
7
6
|
|
|
8
7
|
import { walletFixturePath } from '../../harness-exports.mjs';
|
|
9
|
-
|
|
10
|
-
function fixtureAccountName(base, index, count) {
|
|
11
|
-
if (count === 1) return base || 'account1';
|
|
12
|
-
const match = /^(.*?)(\d+)$/u.exec(base || 'account1');
|
|
13
|
-
return match
|
|
14
|
-
? `${match[1]}${Number(match[2]) + index}`
|
|
15
|
-
: `${base || 'account'}${index + 1}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function mnemonicCount(entry, fixturePath, index) {
|
|
19
|
-
const count = Number(entry.count ?? entry.numberOfAccounts ?? 1);
|
|
20
|
-
if (!Number.isInteger(count) || count < 1 || count > 100) {
|
|
21
|
-
throw new Error(`${fixturePath} accounts[${index}] mnemonic count must be an integer from 1 through 100.`);
|
|
22
|
-
}
|
|
23
|
-
return count;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function deriveFixtureAccounts(fixture, fixturePath) {
|
|
27
|
-
if (!Array.isArray(fixture.accounts) || fixture.accounts.length === 0) {
|
|
28
|
-
throw new Error(`wallet fixture at ${fixturePath} has no accounts array.`);
|
|
29
|
-
}
|
|
30
|
-
return fixture.accounts.flatMap((entry, fixtureIndex) => {
|
|
31
|
-
if (!entry || typeof entry !== 'object' || typeof entry.value !== 'string' || !entry.value.trim()) {
|
|
32
|
-
throw new Error(`${fixturePath} accounts[${fixtureIndex}] must contain a non-empty value.`);
|
|
33
|
-
}
|
|
34
|
-
if (entry.type === 'mnemonic') {
|
|
35
|
-
const count = mnemonicCount(entry, fixturePath, fixtureIndex);
|
|
36
|
-
return Array.from({ length: count }, (_, addressIndex) => {
|
|
37
|
-
const account = mnemonicToAccount(entry.value.trim(), { addressIndex });
|
|
38
|
-
return {
|
|
39
|
-
id: `fixture:${fixtureAccountName(entry.name, addressIndex, count)}`,
|
|
40
|
-
address: account.address,
|
|
41
|
-
name: fixtureAccountName(entry.name, addressIndex, count),
|
|
42
|
-
type: 'eip155:eoa',
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
if (entry.type === 'privateKey') {
|
|
47
|
-
const raw = entry.value.trim();
|
|
48
|
-
const normalized = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
49
|
-
if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
|
|
50
|
-
throw new Error(`${fixturePath} accounts[${fixtureIndex}] privateKey is not a 32-byte hex key.`);
|
|
51
|
-
}
|
|
52
|
-
const account = privateKeyToAccount(normalized);
|
|
53
|
-
const name = entry.name || `account${fixtureIndex + 1}`;
|
|
54
|
-
return [{ id: `fixture:${name}`, address: account.address, name, type: 'eip155:eoa' }];
|
|
55
|
-
}
|
|
56
|
-
throw new Error(`${fixturePath} accounts[${fixtureIndex}].type must be mnemonic or privateKey.`);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
8
|
+
import { readFixtureAccounts } from './_fixture-accounts.mjs';
|
|
59
9
|
|
|
60
10
|
async function listAccounts(input) {
|
|
61
11
|
const scope = input.node?.scope == null ? 'named' : String(input.node.scope);
|
|
62
12
|
if (scope !== 'selected' && scope !== 'named' && scope !== 'evm' && scope !== 'all') {
|
|
63
13
|
throw new Error('metamask.wallet.list_accounts scope must be selected, named, evm, or all.');
|
|
64
14
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
15
|
+
const loaded = await readFixtureAccounts(input.context.projectRoot);
|
|
16
|
+
if (!loaded) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`no wallet fixture at ${walletFixturePath(input.context.projectRoot)}.\nNext: run mm-harness fixtures set`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
67
21
|
const selectedName = String(input.node?.account_name ?? input.node?.account ?? 'dev1');
|
|
68
|
-
const accounts =
|
|
69
|
-
.map((
|
|
70
|
-
|
|
71
|
-
|
|
22
|
+
const accounts = loaded.accounts
|
|
23
|
+
.map(({ name, address }) => ({
|
|
24
|
+
id: `fixture:${name}`,
|
|
25
|
+
address,
|
|
26
|
+
name,
|
|
27
|
+
type: 'eip155:eoa',
|
|
28
|
+
selected: name === selectedName,
|
|
72
29
|
}))
|
|
73
30
|
.filter((account) => {
|
|
74
31
|
if (scope === 'selected') return account.selected;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import fs from 'node:fs';
|
|
16
16
|
import path from 'node:path';
|
|
17
17
|
|
|
18
|
-
import { recipeRuntimeDir } from '
|
|
18
|
+
import { recipeRuntimeDir } from '@farmslot/recipe-cli/harness';
|
|
19
19
|
|
|
20
20
|
export const EXTENSION_SET_UNSUPPORTED_REASON =
|
|
21
21
|
'The extension exposes no runtime remote feature-flag override RPC: RemoteFeatureFlagController.setFlagOverride is never registered in metamask-controller.js getApi(), so stateHooks.submitRequestToBackground cannot reach it. Overrides are pinned into the per-run dist snapshot manifest at launch.';
|
|
@@ -20,14 +20,8 @@ import { spawn } from 'node:child_process';
|
|
|
20
20
|
import { createRequire } from 'node:module';
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
importRecipeHarnessRuntimeCdp,
|
|
26
|
-
extensionIdPath,
|
|
27
|
-
recipeHarnessPath,
|
|
28
|
-
walletFixturePath,
|
|
29
|
-
captureActiveRecipeRecordingSnapshot,
|
|
30
|
-
} from '../../harness-exports.mjs';
|
|
23
|
+
import { captureActiveRecipeRecordingSnapshot, recipeHarnessPath } from '@farmslot/recipe-cli/harness';
|
|
24
|
+
import { importRecipeHarnessRuntimeBrowserExtension, importRecipeHarnessRuntimeCdp, extensionIdPath, walletFixturePath } from '../../harness-exports.mjs';
|
|
31
25
|
|
|
32
26
|
// Resolve the Farmslot harness through normal package dependencies by default.
|
|
33
27
|
// Local Farmslot source is only a dev override handled by the harness paths module.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Bridge from library actions to the harness path
|
|
1
|
+
// Bridge from library actions to the harness path helpers. Published
|
|
2
2
|
// installs load the compiled dist (node cannot type-strip .ts under
|
|
3
3
|
// node_modules); source checkouts without dist fall back to src. Deliberately
|
|
4
4
|
// narrow — importing the package entry here would close an import cycle
|
|
@@ -14,20 +14,15 @@ async function load(distRelative, srcRelative) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const paths = await load('dist/paths.js', 'src/paths.ts');
|
|
17
|
-
const recording = await load('dist/run-recording.js', 'src/run-recording.ts');
|
|
18
17
|
const featureFlags = await load('dist/feature-flags.js', 'src/feature-flags.ts');
|
|
19
18
|
|
|
20
19
|
export const {
|
|
21
20
|
importRecipeHarnessRuntimeBrowserExtension,
|
|
22
21
|
importRecipeHarnessRuntimeCdp,
|
|
23
22
|
extensionIdPath,
|
|
24
|
-
recipeHarnessPath,
|
|
25
|
-
recipeRuntimeDir,
|
|
26
23
|
walletFixturePath,
|
|
27
24
|
} = paths;
|
|
28
25
|
|
|
29
|
-
export const { captureActiveRecipeRecordingSnapshot } = recording;
|
|
30
|
-
|
|
31
26
|
// One normalisation for --remote-flag specs, fixture objects, and the
|
|
32
27
|
// doctor/status summary line. See src/feature-flags.ts.
|
|
33
28
|
export const {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import { execFile } from 'node:child_process';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
|
|
6
|
-
import { resolveMobileToolPath } from '
|
|
6
|
+
import { resolveMobileToolPath } from '@farmslot/adapter-rn';
|
|
7
|
+
|
|
8
|
+
import { nativeAppId } from '../platform/native-session-name.mjs';
|
|
7
9
|
|
|
8
10
|
const execFileAsync = promisify(execFile);
|
|
9
11
|
|
|
@@ -31,13 +33,9 @@ function selectedAndroidSerial(input) {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
function selectedAndroidPackage(input) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
input.context?.env?.ANDROID_PACKAGE_ID ??
|
|
38
|
-
process.env.ANDROID_PACKAGE_ID ??
|
|
39
|
-
'io.metamask',
|
|
40
|
-
).trim();
|
|
36
|
+
const explicit = input.node?.package_id ?? input.node?.app_id;
|
|
37
|
+
if (explicit != null) return String(explicit).trim();
|
|
38
|
+
return nativeAppId({ ...process.env, ...(input.context?.env ?? {}) }, 'android');
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
async function defaultRun(file, args) {
|
|
@@ -4,7 +4,7 @@ import { execFile } from 'node:child_process';
|
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
|
|
6
6
|
import { runAdapter } from '../platform/bridge.mjs';
|
|
7
|
-
import { resolveMobileToolPath } from '
|
|
7
|
+
import { resolveMobileToolPath } from '@farmslot/adapter-rn';
|
|
8
8
|
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
|
|
@@ -8,10 +8,10 @@ import { promisify } from 'node:util';
|
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import bridgeErrors from '
|
|
12
|
-
import brokerModule from '
|
|
13
|
-
import configModule from '
|
|
14
|
-
import { resolveMobileToolPath } from '
|
|
11
|
+
import bridgeErrors from '@farmslot/adapter-rn/bridge-runtime/lib/bridge-errors.cjs';
|
|
12
|
+
import brokerModule from '@farmslot/recipe-runner/cdp-broker';
|
|
13
|
+
import configModule from '@farmslot/adapter-rn/bridge-runtime/lib/config.cjs';
|
|
14
|
+
import { resolveMobileToolPath } from '@farmslot/adapter-rn';
|
|
15
15
|
|
|
16
16
|
const {
|
|
17
17
|
BRIDGE_ERROR_CODES,
|
|
@@ -155,8 +155,8 @@ export async function bridgeEnv(input) {
|
|
|
155
155
|
...(input.context?.env || {}),
|
|
156
156
|
CDP_TIMEOUT: String(input.node?.cdp_timeout_ms ?? process.env.CDP_TIMEOUT ?? '10000'),
|
|
157
157
|
};
|
|
158
|
-
if (adbPath) env.
|
|
159
|
-
if (idbPath) env.
|
|
158
|
+
if (adbPath) env.RECIPE_RN_ADB_PATH = adbPath;
|
|
159
|
+
if (idbPath) env.RECIPE_RN_IDB_PATH = idbPath;
|
|
160
160
|
const target = resolveMobileTarget(input);
|
|
161
161
|
const requestedPlatform = target.requestedPlatform;
|
|
162
162
|
const watcherPort = target.watcherPort;
|
|
@@ -238,8 +238,8 @@ export function resolveMobileTarget(input) {
|
|
|
238
238
|
input.node?.sim_udid ?? contextEnv.SIM_UDID ?? process.env.SIM_UDID;
|
|
239
239
|
const configuredPlatform = String(
|
|
240
240
|
input.node?.platform ??
|
|
241
|
-
contextEnv.
|
|
242
|
-
process.env.
|
|
241
|
+
contextEnv.RECIPE_RN_EXPLICIT_PLATFORM ??
|
|
242
|
+
process.env.RECIPE_RN_EXPLICIT_PLATFORM ??
|
|
243
243
|
contextEnv.PLATFORM ??
|
|
244
244
|
process.env.PLATFORM ??
|
|
245
245
|
'',
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
// Derive native session names and device-specific state directories, honoring configured overrides.
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// The host adapter supplies the app under test in the action env
|
|
5
|
+
// (ANDROID_PACKAGE_ID / IOS_BUNDLE_ID, display name in
|
|
6
|
+
// METAMASK_RECIPE_MOBILE_APP_LABEL); the library has no default app.
|
|
7
|
+
export function nativeAppId(env, platform) {
|
|
8
|
+
const key = platform === 'android' ? 'ANDROID_PACKAGE_ID' : 'IOS_BUNDLE_ID';
|
|
9
|
+
const app = String(env[key] ?? '').trim();
|
|
10
|
+
if (!app) {
|
|
11
|
+
throw new Error(`Mobile native UI needs the app identity in ${key}. Next: run through mm-harness run or call, or set ${key}.`);
|
|
12
|
+
}
|
|
13
|
+
return app;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function nativeAppLabel(env) {
|
|
17
|
+
const label = String(env.METAMASK_RECIPE_MOBILE_APP_LABEL ?? '').trim();
|
|
18
|
+
if (!label) {
|
|
19
|
+
throw new Error('Mobile native UI needs the app display name in METAMASK_RECIPE_MOBILE_APP_LABEL. Next: run through mm-harness run or call, or set METAMASK_RECIPE_MOBILE_APP_LABEL.');
|
|
20
|
+
}
|
|
21
|
+
return label;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function nativeSessionName(env, device, app) {
|
|
5
25
|
const configured = String(env.METAMASK_RECIPE_MOBILE_SESSION ?? '').trim();
|
|
6
26
|
const source = configured || `mm-harness-${app}-${device}`;
|
|
7
27
|
return source.replace(/[^A-Za-z0-9._-]/gu, '-').slice(0, 120);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Create device-targeted native UI sessions for actions and accessibility snapshots.
|
|
2
2
|
// Opening an instrumented iOS session hides the accessibility-masking HUD.
|
|
3
|
-
import { createAgentDeviceUiTransport } from '@farmslot/
|
|
3
|
+
import { createAgentDeviceUiTransport } from '@farmslot/adapter-rn';
|
|
4
4
|
import { createAgentDeviceClient } from 'agent-device';
|
|
5
5
|
|
|
6
6
|
import { resolveMobileTarget } from './bridge.mjs';
|
|
@@ -8,6 +8,7 @@ import { hideAccessibilityMaskingHud } from './observe-ui.mjs';
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
nativeAgentDeviceStateDir,
|
|
11
|
+
nativeAppId,
|
|
11
12
|
nativeSessionName,
|
|
12
13
|
} from './native-session-name.mjs';
|
|
13
14
|
|
|
@@ -19,9 +20,7 @@ export function createNativeSession(input, domain, sessionSuffix = '') {
|
|
|
19
20
|
? target.adbSerial ?? ''
|
|
20
21
|
: input.node?.sim_udid ?? env.SIM_UDID ?? target.iosSimulator ?? '').trim();
|
|
21
22
|
if (!device) throw new Error(`Mobile ${domain} native actions require one explicit ${platform} device. Next: pass --device <device>.`);
|
|
22
|
-
const app =
|
|
23
|
-
? env.ANDROID_PACKAGE_ID ?? 'io.metamask'
|
|
24
|
-
: env.IOS_BUNDLE_ID ?? 'io.metamask.MetaMask');
|
|
23
|
+
const app = nativeAppId(env, platform);
|
|
25
24
|
const baseSession = nativeSessionName(env, device, app);
|
|
26
25
|
const session = sessionSuffix
|
|
27
26
|
? `${baseSession}-${String(sessionSuffix).replace(/[^A-Za-z0-9._-]/gu, '-')}`.slice(0, 120)
|
|
@@ -7,9 +7,11 @@ import { createAgentDeviceClient } from 'agent-device';
|
|
|
7
7
|
import { bridgeCommand } from './bridge.mjs';
|
|
8
8
|
import {
|
|
9
9
|
nativeAgentDeviceStateDir,
|
|
10
|
+
nativeAppId,
|
|
11
|
+
nativeAppLabel,
|
|
10
12
|
nativeSessionName,
|
|
11
13
|
} from './native-session-name.mjs';
|
|
12
|
-
import { mobileToolRecovery, resolveMobileToolPath } from '
|
|
14
|
+
import { mobileToolRecovery, resolveMobileToolPath } from '@farmslot/adapter-rn';
|
|
13
15
|
|
|
14
16
|
const execFileAsync = promisify(execFile);
|
|
15
17
|
const VISIBLE_LIMIT = 200;
|
|
@@ -93,8 +95,13 @@ async function readNativeHierarchy(payload, context) {
|
|
|
93
95
|
env.METAMASK_RECIPE_PROJECT_ROOT = String(context.projectRoot);
|
|
94
96
|
}
|
|
95
97
|
const platform = resolvePlatform(node, env);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
const appLabel = nativeAppLabel(env);
|
|
99
|
+
const hierarchy = platform === 'android'
|
|
100
|
+
? await readAndroidHierarchy(node, env)
|
|
101
|
+
: await readIosHierarchy(node, env);
|
|
102
|
+
// No screen name and no labelled item: name the screen after the app.
|
|
103
|
+
hierarchy.screen.name ??= appLabel;
|
|
104
|
+
return hierarchy;
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
async function readIosHierarchy(node, env) {
|
|
@@ -166,7 +173,7 @@ async function readAndroidHierarchy(node, env) {
|
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
async function readAndroidAgentDeviceHierarchy(serial, env) {
|
|
169
|
-
const app =
|
|
176
|
+
const app = nativeAppId(env, 'android');
|
|
170
177
|
const session = nativeSessionName(env, serial, app);
|
|
171
178
|
const stateDir = nativeAgentDeviceStateDir(env, env.METAMASK_RECIPE_PROJECT_ROOT ?? process.cwd(), serial);
|
|
172
179
|
const client = createAgentDeviceClient({
|
|
@@ -257,8 +264,7 @@ function normalizedHierarchy(
|
|
|
257
264
|
const items = nodes.slice(0, VISIBLE_LIMIT);
|
|
258
265
|
const hiddenOrOffscreen = hiddenNodes.slice(0, HIDDEN_LIMIT);
|
|
259
266
|
const name = metadata.screenName ??
|
|
260
|
-
items.find((item) => item.label)?.label
|
|
261
|
-
'MetaMask';
|
|
267
|
+
items.find((item) => item.label)?.label;
|
|
262
268
|
return {
|
|
263
269
|
screen: {
|
|
264
270
|
provider,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// Navigate Android Home, Swap, and Perps through native controls and verify visible destinations.
|
|
2
2
|
// Switch Perps Lite or Pro mode through visible controls on Android and iOS.
|
|
3
|
-
import { createAgentDeviceUiTransport } from '@farmslot/
|
|
3
|
+
import { createAgentDeviceUiTransport } from '@farmslot/adapter-rn';
|
|
4
4
|
import { createAgentDeviceClient } from 'agent-device';
|
|
5
5
|
import {
|
|
6
6
|
nativeAgentDeviceStateDir,
|
|
7
|
+
nativeAppId,
|
|
7
8
|
nativeSessionName,
|
|
8
9
|
} from '../platform/native-session-name.mjs';
|
|
9
10
|
import { bridgeCommand } from '../platform/bridge.mjs';
|
|
@@ -51,7 +52,7 @@ export async function navigateOpaqueMobile(input, destination) {
|
|
|
51
52
|
if (!device) {
|
|
52
53
|
throw new Error('Opaque Mobile navigation requires one explicit Android device serial.');
|
|
53
54
|
}
|
|
54
|
-
const app =
|
|
55
|
+
const app = nativeAppId(env, 'android');
|
|
55
56
|
const session = nativeSessionName(env, device, app);
|
|
56
57
|
const stateDir = nativeAgentDeviceStateDir(env, input.context.projectRoot, device);
|
|
57
58
|
const client = createAgentDeviceClient({
|
|
@@ -184,7 +185,7 @@ export async function ensureVisibleAndroidPerpsMode(input, perpsMode) {
|
|
|
184
185
|
'Android Perps mode requires one explicit device serial.',
|
|
185
186
|
);
|
|
186
187
|
}
|
|
187
|
-
const app =
|
|
188
|
+
const app = nativeAppId(env, 'android');
|
|
188
189
|
const session = nativeSessionName(env, device, app);
|
|
189
190
|
const stateDir = nativeAgentDeviceStateDir(env, input.context.projectRoot, device);
|
|
190
191
|
const client = createAgentDeviceClient({
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// Drive Android wallet onboarding, lock, unlock, and reset through native accessibility.
|
|
2
2
|
// Wallet observations can navigate home and open the address list to verify the selected account.
|
|
3
|
-
import { createAgentDeviceUiTransport } from '@farmslot/
|
|
3
|
+
import { createAgentDeviceUiTransport } from '@farmslot/adapter-rn';
|
|
4
4
|
import { createAgentDeviceClient } from 'agent-device';
|
|
5
5
|
import {
|
|
6
6
|
nativeAgentDeviceStateDir,
|
|
7
|
+
nativeAppId,
|
|
7
8
|
nativeSessionName,
|
|
8
9
|
} from '../platform/native-session-name.mjs';
|
|
9
10
|
|
|
@@ -249,7 +250,7 @@ function nativeSession(input) {
|
|
|
249
250
|
const env = { ...process.env, ...(input.context?.env ?? {}) };
|
|
250
251
|
const device = String(env.ADB_SERIAL ?? env.ANDROID_SERIAL ?? '').trim();
|
|
251
252
|
if (!device) throw new Error('Opaque Mobile UI execution requires one explicit Android device serial.');
|
|
252
|
-
const app =
|
|
253
|
+
const app = nativeAppId(env, 'android');
|
|
253
254
|
const session = nativeSessionName(env, device, app);
|
|
254
255
|
const stateDir = nativeAgentDeviceStateDir(env, input.context.projectRoot, device);
|
|
255
256
|
const client = createAgentDeviceClient({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Minimal adapter IO for the shared analytics actions.
|
|
2
2
|
//
|
|
3
|
-
// Mirrors the live-adapter contract in
|
|
3
|
+
// Mirrors the live-adapter contract in @farmslot/recipe-cli/harness. Neither
|
|
4
4
|
// existing runAdapter fits: extension's lives under actions/extension/platform
|
|
5
5
|
// and core's carries PerpsController teardown and expect_error handling. A
|
|
6
6
|
// `shared/` action importing either would couple every platform to one of them,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The one console matcher: classify application console events and apply a
|
|
2
2
|
// recipe-library allowlist of known noise. Used by run diagnostics
|
|
3
|
-
// (src/
|
|
3
|
+
// (src/console-classifier.ts), the assert_no_console_errors action, the
|
|
4
4
|
// allowlist generator, and the recipe library's checks/console-guard.mjs
|
|
5
5
|
// (which imports this file), so every caller judges an event the same way.
|
|
6
6
|
//
|
|
@@ -9,7 +9,8 @@ import { pathToFileURL } from 'node:url';
|
|
|
9
9
|
|
|
10
10
|
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
11
11
|
|
|
12
|
-
import { recipeRuntimeDir
|
|
12
|
+
import { recipeRuntimeDir } from '@farmslot/recipe-cli/harness';
|
|
13
|
+
import { walletFixturePath } from '../../harness-exports.mjs';
|
|
13
14
|
|
|
14
15
|
export const SIGNER_MODES = Object.freeze(['extension', 'injected']);
|
|
15
16
|
export const DEFAULT_ACCOUNT = 'dev1';
|