@deeeed/metamask-harness 0.17.5 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +872 -0
- package/README.md +94 -140
- package/adapters/extension/console-tail.mjs +55 -24
- package/adapters/extension/ensure-browser.sh +6 -1
- package/adapters/extension/inject.mjs +6 -9
- package/adapters/extension/launch-browser.cjs +7 -1
- package/adapters/extension/launch-webpack.cjs +83 -0
- package/adapters/extension/launch.sh +8 -11
- package/adapters/extension/lib/chrome-args.cjs +8 -1
- package/adapters/extension/lib/macos-focus.cjs +32 -0
- package/adapters/extension/live.sh +12 -21
- package/adapters/extension/reattach.sh +1 -0
- package/adapters/extension/seed-fixture.sh +4 -12
- package/adapters/extension/sidepanel-toggle.sh +4 -1
- package/adapters/extension/stamp-runtime-title.cjs +58 -0
- package/adapters/extension/start-watch.sh +17 -4
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/sync-webpack-dist.cjs +107 -0
- package/adapters/extension/verify.sh +1 -1
- package/adapters/manifest.json +53 -37
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +16 -0
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +3 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
- package/adapters/mobile/cleanup.sh +16 -176
- package/adapters/mobile/inject.sh +90 -671
- package/adapters/mobile/launch-console-forwarder.cjs +39 -0
- package/adapters/mobile/open-device.sh +45 -7
- package/adapters/mobile/start-console-forwarder.sh +70 -0
- package/adapters/mobile/start-metro.sh +0 -41
- package/adapters/mobile/verify.sh +19 -64
- package/adapters/mobile/wait-for-bridge.sh +22 -8
- package/adapters/shared/resolve-slot-ports-core.mjs +17 -6
- package/adapters/shared/resolve-slot-ports.sh +17 -5
- package/adapters/shared/update-check-worker.mjs +43 -0
- package/bin/mm-harness +21 -2
- package/dist/adapters/core/surface.js +4 -1
- package/dist/adapters/extension/console-capture.js +95 -0
- package/dist/adapters/extension/product-config.js +110 -0
- package/dist/adapters/extension/runtime-decision.js +20 -2
- package/dist/adapters/extension/surface.js +22 -3
- package/dist/adapters/mobile/perps-env.js +43 -3
- package/dist/adapters/mobile/prepare.js +39 -7
- package/dist/adapters/mobile/surface.js +5 -2
- package/dist/adapters/resolve-slot-ports.js +2 -2
- package/dist/adapters/slot-ports.js +13 -16
- package/dist/adapters.js +56 -16
- package/dist/checkout-lock.js +27 -2
- package/dist/cli-color.js +19 -0
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +7 -14
- package/dist/command-contract.js +451 -0
- package/dist/command-journal.js +225 -0
- package/dist/commands/call.js +170 -50
- package/dist/commands/check.js +9 -3
- package/dist/commands/completion-candidates.js +20 -13
- package/dist/commands/device-target.js +27 -12
- package/dist/commands/doctor.js +106 -25
- package/dist/commands/fixtures.js +92 -34
- package/dist/commands/flows.js +39 -10
- package/dist/commands/last.js +52 -0
- package/dist/commands/launch/extension.js +38 -15
- package/dist/commands/launch/index.js +207 -63
- package/dist/commands/list-executables.js +151 -29
- package/dist/commands/logs.js +8 -6
- package/dist/commands/manifest.js +270 -35
- package/dist/commands/parse-args.js +13 -1
- package/dist/commands/provision.js +10 -3
- package/dist/commands/run-engine.js +435 -105
- package/dist/commands/run-report.js +12 -3
- package/dist/commands/run.js +355 -50
- package/dist/commands/shared.js +75 -3
- package/dist/commands/status-probe.js +4 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/stop.js +7 -2
- package/dist/commands/update.js +16 -28
- package/dist/completions-cache.js +1 -1
- package/dist/doctor.js +57 -10
- package/dist/harness.js +47 -137
- package/dist/heal-bounds.js +2 -2
- package/dist/json-stream.js +57 -0
- package/dist/live-adapter-contract.js +138 -32
- package/dist/manifest.js +161 -1
- package/dist/mm-harness-cli.js +135 -54
- package/dist/paths.js +2 -5
- package/dist/recipe-security.js +178 -0
- package/dist/run-diagnostics.js +261 -0
- package/dist/runner.js +117 -8
- package/docs/CONTRIBUTING.md +137 -0
- package/docs/QA.md +185 -0
- package/docs/RECIPES.md +161 -0
- package/docs/SECURITY.md +88 -0
- package/library/README.md +4 -0
- package/library/actions/core/perps/_controller.mjs +10 -55
- package/library/actions/core/perps/read_account.mjs +2 -2
- package/library/actions/core/perps/read_orders.mjs +2 -1
- package/library/actions/core/perps/read_positions.mjs +2 -1
- package/library/actions/core/wallet/list_accounts.mjs +95 -0
- package/library/actions/extension/platform/cdp.mjs +1 -0
- package/library/actions/extension/wallet/list_accounts.mjs +41 -0
- package/library/actions/mobile/wallet/list_accounts.mjs +37 -0
- package/library/manifests/core.action-manifest.json +68 -7
- package/library/manifests/extension.action-manifest.json +53 -0
- package/library/manifests/mobile.action-manifest.json +81 -3
- package/library/recipes/runner/action-validation.extension.recipe.json +8 -1
- package/library/recipes/runner/action-validation.mobile.recipe.json +8 -1
- package/library/recipes/runner/smoke.core.recipe.json +27 -0
- package/library/recipes/wallet/smoke.extension.recipe.json +42 -0
- package/library/recipes/wallet/smoke.mobile.recipe.json +42 -0
- package/package.json +12 -5
- package/scripts/completions.sh +7 -7
- package/scripts/validate-human-outcomes.mjs +169 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
- package/adapters/mobile/overlay/compat/README.md +0 -28
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
- package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
- package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
- package/adapters/shared/resolve-farmslot-ports.sh +0 -7
- package/docs/ADAPTER-SURFACE.md +0 -119
- package/docs/CHEATSHEET.md +0 -61
- package/docs/CLI-SPEC.md +0 -1098
- package/docs/CODE-MAP.md +0 -62
- package/docs/DEBUG-HANDOVER.md +0 -36
- package/docs/MENTAL-MODEL.md +0 -295
- package/docs/UX-PRINCIPLES.md +0 -64
- package/docs/architecture.md +0 -398
- package/docs/live-adapter-contract.md +0 -188
- package/docs/package-boundaries.md +0 -47
- package/docs/perps-flow-catalog.md +0 -235
- package/docs/recipe-libraries.md +0 -301
- package/docs/runtime-file-conventions.md +0 -36
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getCoreController, runAdapter } from './_controller.mjs';
|
|
1
|
+
import { getCoreController, isDirectRun, runAdapter } from './_controller.mjs';
|
|
2
2
|
|
|
3
3
|
function redactAccount(account) {
|
|
4
4
|
return {
|
|
@@ -27,4 +27,4 @@ export async function readAccount(input) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
runAdapter(readAccount);
|
|
30
|
+
if (isDirectRun(import.meta.url)) runAdapter(readAccount);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getCoreController,
|
|
3
|
+
isDirectRun,
|
|
3
4
|
redactPosition,
|
|
4
5
|
runAdapter,
|
|
5
6
|
selectedItems,
|
|
@@ -24,4 +25,4 @@ export async function readPositions(input) {
|
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
runAdapter(readPositions);
|
|
28
|
+
if (isDirectRun(import.meta.url)) runAdapter(readPositions);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
|
|
5
|
+
import { walletFixturePath } from '../../harness-exports.mjs';
|
|
6
|
+
|
|
7
|
+
function fixtureAccountName(base, index, count) {
|
|
8
|
+
if (count === 1) return base || 'account1';
|
|
9
|
+
const match = /^(.*?)(\d+)$/u.exec(base || 'account1');
|
|
10
|
+
return match
|
|
11
|
+
? `${match[1]}${Number(match[2]) + index}`
|
|
12
|
+
: `${base || 'account'}${index + 1}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function mnemonicCount(entry, fixturePath, index) {
|
|
16
|
+
const count = Number(entry.count ?? entry.numberOfAccounts ?? 1);
|
|
17
|
+
if (!Number.isInteger(count) || count < 1 || count > 100) {
|
|
18
|
+
throw new Error(`${fixturePath} accounts[${index}] mnemonic count must be an integer from 1 through 100.`);
|
|
19
|
+
}
|
|
20
|
+
return count;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function deriveFixtureAccounts(fixture, fixturePath) {
|
|
24
|
+
if (!Array.isArray(fixture.accounts) || fixture.accounts.length === 0) {
|
|
25
|
+
throw new Error(`wallet fixture at ${fixturePath} has no accounts array.`);
|
|
26
|
+
}
|
|
27
|
+
return fixture.accounts.flatMap((entry, fixtureIndex) => {
|
|
28
|
+
if (!entry || typeof entry !== 'object' || typeof entry.value !== 'string' || !entry.value.trim()) {
|
|
29
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}] must contain a non-empty value.`);
|
|
30
|
+
}
|
|
31
|
+
if (entry.type === 'mnemonic') {
|
|
32
|
+
const count = mnemonicCount(entry, fixturePath, fixtureIndex);
|
|
33
|
+
return Array.from({ length: count }, (_, addressIndex) => {
|
|
34
|
+
const account = mnemonicToAccount(entry.value.trim(), { addressIndex });
|
|
35
|
+
return {
|
|
36
|
+
id: `fixture:${fixtureAccountName(entry.name, addressIndex, count)}`,
|
|
37
|
+
address: account.address,
|
|
38
|
+
name: fixtureAccountName(entry.name, addressIndex, count),
|
|
39
|
+
type: 'eip155:eoa',
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (entry.type === 'privateKey') {
|
|
44
|
+
const raw = entry.value.trim();
|
|
45
|
+
const normalized = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
46
|
+
if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
|
|
47
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}] privateKey is not a 32-byte hex key.`);
|
|
48
|
+
}
|
|
49
|
+
const account = privateKeyToAccount(normalized);
|
|
50
|
+
const name = entry.name || `account${fixtureIndex + 1}`;
|
|
51
|
+
return [{ id: `fixture:${name}`, address: account.address, name, type: 'eip155:eoa' }];
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`${fixturePath} accounts[${fixtureIndex}].type must be mnemonic or privateKey.`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function listAccounts(input) {
|
|
58
|
+
const scope = input.node?.scope == null ? 'named' : String(input.node.scope);
|
|
59
|
+
if (scope !== 'selected' && scope !== 'named' && scope !== 'evm' && scope !== 'all') {
|
|
60
|
+
throw new Error('metamask.wallet.list_accounts scope must be selected, named, evm, or all.');
|
|
61
|
+
}
|
|
62
|
+
const fixturePath = walletFixturePath(input.context.projectRoot);
|
|
63
|
+
const fixture = JSON.parse(await readFile(fixturePath, 'utf8'));
|
|
64
|
+
const selectedName = String(input.node?.account_name ?? input.node?.account ?? 'dev1');
|
|
65
|
+
const accounts = deriveFixtureAccounts(fixture, fixturePath)
|
|
66
|
+
.map((account) => ({
|
|
67
|
+
...account,
|
|
68
|
+
selected: account.name === selectedName,
|
|
69
|
+
}))
|
|
70
|
+
.filter((account) => {
|
|
71
|
+
if (scope === 'selected') return account.selected;
|
|
72
|
+
if (scope === 'all') return true;
|
|
73
|
+
if (scope === 'evm') return /^0x[0-9a-f]{40}$/iu.test(account.address);
|
|
74
|
+
return account.selected || Boolean(account.name.trim());
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
action: input.action,
|
|
78
|
+
source: 'core-headless-wallet-fixture',
|
|
79
|
+
scope,
|
|
80
|
+
accounts,
|
|
81
|
+
count: accounts.length,
|
|
82
|
+
redacted: true,
|
|
83
|
+
selectionSource: 'explicit-controller-account-input',
|
|
84
|
+
proofPath: 'core-wallet-fixture-accounts',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
89
|
+
const inputPath = process.argv[2] || process.env.METAMASK_RECIPE_ADAPTER_INPUT;
|
|
90
|
+
if (!inputPath) throw new Error('Adapter input path is required.');
|
|
91
|
+
const input = JSON.parse(await readFile(inputPath, 'utf8'));
|
|
92
|
+
const result = await listAccounts(input);
|
|
93
|
+
if (input.outputPath) await writeFile(input.outputPath, `${JSON.stringify(result, null, 2)}\n`);
|
|
94
|
+
else process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
95
|
+
}
|
|
@@ -397,6 +397,7 @@ async function launchExistingDistRuntime(input, port) {
|
|
|
397
397
|
try {
|
|
398
398
|
child = spawn(chrome, [
|
|
399
399
|
`--user-data-dir=${profileDir}`,
|
|
400
|
+
...(process.platform === 'darwin' ? ['--use-mock-keychain'] : []),
|
|
400
401
|
'--remote-debugging-address=127.0.0.1',
|
|
401
402
|
`--remote-debugging-port=${port}`,
|
|
402
403
|
'--no-first-run',
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
|
+
|
|
3
|
+
runAdapter((input) => withExtensionPage(input, async (page) => {
|
|
4
|
+
const scope = input.node?.scope == null ? 'named' : String(input.node.scope);
|
|
5
|
+
if (scope !== 'selected' && scope !== 'named' && scope !== 'evm' && scope !== 'all') {
|
|
6
|
+
throw new Error('metamask.wallet.list_accounts scope must be selected, named, evm, or all.');
|
|
7
|
+
}
|
|
8
|
+
const result = await page.evaluate(`(() => {
|
|
9
|
+
const store = globalThis.stateHooks?.store;
|
|
10
|
+
if (!store || typeof store.getState !== 'function') {
|
|
11
|
+
throw new Error('stateHooks.store.getState is unavailable.');
|
|
12
|
+
}
|
|
13
|
+
const internal = store.getState()?.metamask?.internalAccounts || {};
|
|
14
|
+
const selectedId = internal.selectedAccount || null;
|
|
15
|
+
const scope = ${JSON.stringify(scope)};
|
|
16
|
+
const accounts = Object.entries(internal.accounts || {})
|
|
17
|
+
.map(([id, account]) => ({
|
|
18
|
+
id,
|
|
19
|
+
address: account?.address || null,
|
|
20
|
+
name: account?.metadata?.name || null,
|
|
21
|
+
type: account?.type || null,
|
|
22
|
+
selected: id === selectedId
|
|
23
|
+
}))
|
|
24
|
+
.filter((account) => {
|
|
25
|
+
if (scope === 'selected') return account.selected;
|
|
26
|
+
if (scope === 'all') return true;
|
|
27
|
+
if (scope === 'evm') return /^0x[0-9a-f]{40}$/iu.test(account.address || '');
|
|
28
|
+
return account.selected || Boolean(String(account.name || '').trim());
|
|
29
|
+
});
|
|
30
|
+
return { accounts };
|
|
31
|
+
})()`);
|
|
32
|
+
return {
|
|
33
|
+
action: input.action,
|
|
34
|
+
source: 'extension-stateHooks-store',
|
|
35
|
+
scope,
|
|
36
|
+
...result,
|
|
37
|
+
count: result.accounts.length,
|
|
38
|
+
redacted: true,
|
|
39
|
+
proofPath: 'extension-wallet-accounts',
|
|
40
|
+
};
|
|
41
|
+
}));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { bridgeCommand, runAdapter } from '../platform/bridge.mjs';
|
|
2
|
+
|
|
3
|
+
runAdapter(async (input) => {
|
|
4
|
+
const scope = input.node?.scope == null ? 'named' : String(input.node.scope);
|
|
5
|
+
if (scope !== 'selected' && scope !== 'named' && scope !== 'evm' && scope !== 'all') {
|
|
6
|
+
throw new Error('metamask.wallet.list_accounts scope must be selected, named, evm, or all.');
|
|
7
|
+
}
|
|
8
|
+
const rawAccounts = await bridgeCommand(input, ['list-accounts']);
|
|
9
|
+
if (!Array.isArray(rawAccounts)) {
|
|
10
|
+
throw new Error(`metamask.wallet.list_accounts expected an array, got ${typeof rawAccounts}.`);
|
|
11
|
+
}
|
|
12
|
+
const selected = await bridgeCommand(input, ['get-selected-account']);
|
|
13
|
+
const selectedAddress = String(selected?.address ?? selected ?? '').toLowerCase();
|
|
14
|
+
const accounts = rawAccounts
|
|
15
|
+
.map((account) => ({
|
|
16
|
+
id: account?.id ?? null,
|
|
17
|
+
address: account?.address ?? null,
|
|
18
|
+
name: account?.name ?? null,
|
|
19
|
+
type: account?.type ?? null,
|
|
20
|
+
selected: Boolean(selectedAddress && String(account?.address ?? '').toLowerCase() === selectedAddress),
|
|
21
|
+
}))
|
|
22
|
+
.filter((account) => {
|
|
23
|
+
if (scope === 'selected') return account.selected;
|
|
24
|
+
if (scope === 'all') return true;
|
|
25
|
+
if (scope === 'evm') return /^0x[0-9a-f]{40}$/iu.test(String(account.address ?? ''));
|
|
26
|
+
return account.selected || Boolean(String(account.name ?? '').trim());
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
action: input.action,
|
|
30
|
+
source: 'mobile-accounts-controller',
|
|
31
|
+
scope,
|
|
32
|
+
accounts,
|
|
33
|
+
count: accounts.length,
|
|
34
|
+
redacted: true,
|
|
35
|
+
proofPath: 'agentic-wallet-accounts',
|
|
36
|
+
};
|
|
37
|
+
});
|
|
@@ -162,6 +162,18 @@
|
|
|
162
162
|
}
|
|
163
163
|
]
|
|
164
164
|
},
|
|
165
|
+
"metamask.wallet.list_accounts": {
|
|
166
|
+
"description": "List redacted headless fixture accounts available to Core controller actions.",
|
|
167
|
+
"examples": [
|
|
168
|
+
{
|
|
169
|
+
"description": "List configured Core accounts",
|
|
170
|
+
"node": {
|
|
171
|
+
"action": "metamask.wallet.list_accounts",
|
|
172
|
+
"intent": "Discover the accounts available to headless controller actions"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
165
177
|
"metamask.perps.read_positions": {
|
|
166
178
|
"description": "Read live Perps positions directly from the perps controller (HyperLiquid testnet, HTTP read-only) and return the selected subset.",
|
|
167
179
|
"examples": [
|
|
@@ -392,6 +404,51 @@
|
|
|
392
404
|
}
|
|
393
405
|
},
|
|
394
406
|
"custom_actions": [
|
|
407
|
+
{
|
|
408
|
+
"name": "metamask.wallet.list_accounts",
|
|
409
|
+
"owner": "metamask",
|
|
410
|
+
"description": "List redacted headless fixture accounts available to Core controller actions.",
|
|
411
|
+
"schema": {
|
|
412
|
+
"type": "object",
|
|
413
|
+
"properties": {
|
|
414
|
+
"action": {
|
|
415
|
+
"const": "metamask.wallet.list_accounts"
|
|
416
|
+
},
|
|
417
|
+
"scope": {
|
|
418
|
+
"type": "string",
|
|
419
|
+
"enum": [
|
|
420
|
+
"selected",
|
|
421
|
+
"named",
|
|
422
|
+
"evm",
|
|
423
|
+
"all"
|
|
424
|
+
],
|
|
425
|
+
"description": "Account scope. Core fixtures are EVM-only today; selected filters to the explicit controller account input."
|
|
426
|
+
},
|
|
427
|
+
"account": {
|
|
428
|
+
"type": "string",
|
|
429
|
+
"description": "Optional account name to mark as selected for the next controller operation; defaults to dev1."
|
|
430
|
+
},
|
|
431
|
+
"account_name": {
|
|
432
|
+
"type": "string",
|
|
433
|
+
"description": "Alias for account."
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"required": [
|
|
437
|
+
"action"
|
|
438
|
+
]
|
|
439
|
+
},
|
|
440
|
+
"examples": [
|
|
441
|
+
{
|
|
442
|
+
"description": "List configured Core accounts",
|
|
443
|
+
"node": {
|
|
444
|
+
"action": "metamask.wallet.list_accounts",
|
|
445
|
+
"intent": "Discover the accounts available to headless controller actions"
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
],
|
|
449
|
+
"proof_effect": "Records the redacted addresses derived from the same wallet fixture used by headless controller actions.",
|
|
450
|
+
"safety_notes": "Read-only fixture metadata; must never return private keys, mnemonics, passwords, or vault data."
|
|
451
|
+
},
|
|
395
452
|
{
|
|
396
453
|
"name": "metamask.perps.read_positions",
|
|
397
454
|
"owner": "metamask",
|
|
@@ -446,7 +503,7 @@
|
|
|
446
503
|
},
|
|
447
504
|
"account": {
|
|
448
505
|
"type": "string",
|
|
449
|
-
"description": "EVM address to read positions for; defaults to
|
|
506
|
+
"description": "EVM address to read positions for; defaults to the canonical wallet fixture account."
|
|
450
507
|
},
|
|
451
508
|
"timeout_ms": {
|
|
452
509
|
"type": "number"
|
|
@@ -523,7 +580,7 @@
|
|
|
523
580
|
},
|
|
524
581
|
"account": {
|
|
525
582
|
"type": "string",
|
|
526
|
-
"description": "EVM address to read orders for; defaults to
|
|
583
|
+
"description": "EVM address to read orders for; defaults to the canonical wallet fixture account."
|
|
527
584
|
},
|
|
528
585
|
"timeout_ms": {
|
|
529
586
|
"type": "number"
|
|
@@ -558,7 +615,7 @@
|
|
|
558
615
|
},
|
|
559
616
|
"account": {
|
|
560
617
|
"type": "string",
|
|
561
|
-
"description": "EVM address to read account state for; defaults to
|
|
618
|
+
"description": "EVM address to read account state for; defaults to the canonical wallet fixture account."
|
|
562
619
|
},
|
|
563
620
|
"timeout_ms": {
|
|
564
621
|
"type": "number"
|
|
@@ -653,7 +710,7 @@
|
|
|
653
710
|
},
|
|
654
711
|
"account": {
|
|
655
712
|
"type": "string",
|
|
656
|
-
"description": "EVM address to trade for; defaults to
|
|
713
|
+
"description": "EVM address to trade for; defaults to the canonical wallet fixture account."
|
|
657
714
|
},
|
|
658
715
|
"timeout_ms": {
|
|
659
716
|
"type": "number"
|
|
@@ -748,7 +805,7 @@
|
|
|
748
805
|
},
|
|
749
806
|
"account": {
|
|
750
807
|
"type": "string",
|
|
751
|
-
"description": "EVM address to close for; defaults to
|
|
808
|
+
"description": "EVM address to close for; defaults to the canonical wallet fixture account."
|
|
752
809
|
},
|
|
753
810
|
"timeout_ms": {
|
|
754
811
|
"type": "number"
|
|
@@ -837,7 +894,7 @@
|
|
|
837
894
|
},
|
|
838
895
|
"account": {
|
|
839
896
|
"type": "string",
|
|
840
|
-
"description": "EVM address to read for; defaults to
|
|
897
|
+
"description": "EVM address to read for; defaults to the canonical wallet fixture account."
|
|
841
898
|
},
|
|
842
899
|
"timeout_ms": {
|
|
843
900
|
"type": "number"
|
|
@@ -937,7 +994,7 @@
|
|
|
937
994
|
},
|
|
938
995
|
"account": {
|
|
939
996
|
"type": "string",
|
|
940
|
-
"description": "EVM address to trade for; defaults to
|
|
997
|
+
"description": "EVM address to trade for; defaults to the canonical wallet fixture account."
|
|
941
998
|
},
|
|
942
999
|
"timeout_ms": {
|
|
943
1000
|
"type": "number"
|
|
@@ -1486,6 +1543,10 @@
|
|
|
1486
1543
|
"action": "app.status",
|
|
1487
1544
|
"implementation": "@metamask/recipe-runner/platform-status"
|
|
1488
1545
|
},
|
|
1546
|
+
{
|
|
1547
|
+
"action": "metamask.wallet.list_accounts",
|
|
1548
|
+
"implementation": "@metamask/recipe-runner/core/wallet"
|
|
1549
|
+
},
|
|
1489
1550
|
{
|
|
1490
1551
|
"action": "metamask.perps.read_positions",
|
|
1491
1552
|
"implementation": "@metamask/recipe-runner/core/perps"
|
|
@@ -516,6 +516,18 @@
|
|
|
516
516
|
}
|
|
517
517
|
]
|
|
518
518
|
},
|
|
519
|
+
"metamask.wallet.list_accounts": {
|
|
520
|
+
"description": "List redacted wallet accounts and identify the selected account without mutating the app.",
|
|
521
|
+
"examples": [
|
|
522
|
+
{
|
|
523
|
+
"description": "List wallet accounts",
|
|
524
|
+
"node": {
|
|
525
|
+
"action": "metamask.wallet.list_accounts",
|
|
526
|
+
"intent": "Discover the available wallet accounts"
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
]
|
|
530
|
+
},
|
|
519
531
|
"metamask.wallet.read_state": {
|
|
520
532
|
"description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
|
|
521
533
|
"examples": [
|
|
@@ -911,6 +923,43 @@
|
|
|
911
923
|
],
|
|
912
924
|
"owner": "metamask"
|
|
913
925
|
},
|
|
926
|
+
{
|
|
927
|
+
"name": "metamask.wallet.list_accounts",
|
|
928
|
+
"owner": "metamask",
|
|
929
|
+
"description": "List redacted wallet accounts and identify the selected account without mutating the app.",
|
|
930
|
+
"schema": {
|
|
931
|
+
"type": "object",
|
|
932
|
+
"properties": {
|
|
933
|
+
"action": {
|
|
934
|
+
"const": "metamask.wallet.list_accounts"
|
|
935
|
+
},
|
|
936
|
+
"scope": {
|
|
937
|
+
"type": "string",
|
|
938
|
+
"enum": [
|
|
939
|
+
"selected",
|
|
940
|
+
"named",
|
|
941
|
+
"evm",
|
|
942
|
+
"all"
|
|
943
|
+
],
|
|
944
|
+
"description": "Account scope. Defaults to named human-visible accounts; use selected for the active account, evm for EVM accounts, or all for the full inventory."
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
"required": [
|
|
948
|
+
"action"
|
|
949
|
+
]
|
|
950
|
+
},
|
|
951
|
+
"examples": [
|
|
952
|
+
{
|
|
953
|
+
"description": "List wallet accounts",
|
|
954
|
+
"node": {
|
|
955
|
+
"action": "metamask.wallet.list_accounts",
|
|
956
|
+
"intent": "Discover the available wallet accounts"
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
],
|
|
960
|
+
"proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
|
|
961
|
+
"safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials."
|
|
962
|
+
},
|
|
914
963
|
{
|
|
915
964
|
"name": "metamask.wallet.read_state",
|
|
916
965
|
"owner": "metamask",
|
|
@@ -2045,6 +2094,10 @@
|
|
|
2045
2094
|
"action": "metamask.wallet.select_account",
|
|
2046
2095
|
"implementation": "@metamask/recipe-runner/extension/wallet"
|
|
2047
2096
|
},
|
|
2097
|
+
{
|
|
2098
|
+
"action": "metamask.wallet.list_accounts",
|
|
2099
|
+
"implementation": "@metamask/recipe-runner/extension/wallet"
|
|
2100
|
+
},
|
|
2048
2101
|
{
|
|
2049
2102
|
"action": "metamask.wallet.read_state",
|
|
2050
2103
|
"implementation": "@metamask/recipe-runner/extension/wallet"
|
|
@@ -156,13 +156,38 @@
|
|
|
156
156
|
]
|
|
157
157
|
},
|
|
158
158
|
"ui.press": {
|
|
159
|
-
"description": "Press
|
|
159
|
+
"description": "Press a React Native component by exact testID/test_id, selector identifier, or contained visible text.",
|
|
160
|
+
"schema": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"properties": {
|
|
163
|
+
"action": { "const": "ui.press" },
|
|
164
|
+
"test_id": { "type": "string", "minLength": 1 },
|
|
165
|
+
"testID": { "type": "string", "minLength": 1 },
|
|
166
|
+
"selector": { "type": "string", "minLength": 1 },
|
|
167
|
+
"text": { "type": "string", "minLength": 1 }
|
|
168
|
+
},
|
|
169
|
+
"oneOf": [
|
|
170
|
+
{ "required": ["test_id"] },
|
|
171
|
+
{ "required": ["testID"] },
|
|
172
|
+
{ "required": ["selector"] },
|
|
173
|
+
{ "required": ["text"] }
|
|
174
|
+
]
|
|
175
|
+
},
|
|
160
176
|
"examples": [
|
|
161
177
|
{
|
|
178
|
+
"description": "Press by stable React Native testID",
|
|
162
179
|
"node": {
|
|
163
180
|
"action": "ui.press",
|
|
164
|
-
"
|
|
165
|
-
"intent": "
|
|
181
|
+
"test_id": "perps-tab",
|
|
182
|
+
"intent": "Open Perps through its stable testID"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"description": "Press a component that is reachable only by visible text",
|
|
187
|
+
"node": {
|
|
188
|
+
"action": "ui.press",
|
|
189
|
+
"text": "Ethereum",
|
|
190
|
+
"intent": "Press the component containing the visible Ethereum label"
|
|
166
191
|
}
|
|
167
192
|
}
|
|
168
193
|
]
|
|
@@ -525,6 +550,18 @@
|
|
|
525
550
|
}
|
|
526
551
|
]
|
|
527
552
|
},
|
|
553
|
+
"metamask.wallet.list_accounts": {
|
|
554
|
+
"description": "List redacted wallet accounts and identify the selected account without mutating the app.",
|
|
555
|
+
"examples": [
|
|
556
|
+
{
|
|
557
|
+
"description": "List wallet accounts",
|
|
558
|
+
"node": {
|
|
559
|
+
"action": "metamask.wallet.list_accounts",
|
|
560
|
+
"intent": "Discover the available wallet accounts"
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
},
|
|
528
565
|
"metamask.wallet.read_state": {
|
|
529
566
|
"description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
|
|
530
567
|
"examples": [
|
|
@@ -922,6 +959,43 @@
|
|
|
922
959
|
"proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
|
|
923
960
|
"safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
|
|
924
961
|
},
|
|
962
|
+
{
|
|
963
|
+
"name": "metamask.wallet.list_accounts",
|
|
964
|
+
"owner": "metamask",
|
|
965
|
+
"description": "List redacted wallet accounts and identify the selected account without mutating the app.",
|
|
966
|
+
"schema": {
|
|
967
|
+
"type": "object",
|
|
968
|
+
"properties": {
|
|
969
|
+
"action": {
|
|
970
|
+
"const": "metamask.wallet.list_accounts"
|
|
971
|
+
},
|
|
972
|
+
"scope": {
|
|
973
|
+
"type": "string",
|
|
974
|
+
"enum": [
|
|
975
|
+
"selected",
|
|
976
|
+
"named",
|
|
977
|
+
"evm",
|
|
978
|
+
"all"
|
|
979
|
+
],
|
|
980
|
+
"description": "Account scope. Defaults to named human-visible accounts; use selected for the active account, evm for EVM accounts, or all for the full inventory."
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
"required": [
|
|
984
|
+
"action"
|
|
985
|
+
]
|
|
986
|
+
},
|
|
987
|
+
"examples": [
|
|
988
|
+
{
|
|
989
|
+
"description": "List wallet accounts",
|
|
990
|
+
"node": {
|
|
991
|
+
"action": "metamask.wallet.list_accounts",
|
|
992
|
+
"intent": "Discover the available wallet accounts"
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
],
|
|
996
|
+
"proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
|
|
997
|
+
"safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials."
|
|
998
|
+
},
|
|
925
999
|
{
|
|
926
1000
|
"name": "metamask.wallet.read_state",
|
|
927
1001
|
"owner": "metamask",
|
|
@@ -2067,6 +2141,10 @@
|
|
|
2067
2141
|
"action": "metamask.wallet.select_account",
|
|
2068
2142
|
"implementation": "@metamask/recipe-runner/mobile/wallet"
|
|
2069
2143
|
},
|
|
2144
|
+
{
|
|
2145
|
+
"action": "metamask.wallet.list_accounts",
|
|
2146
|
+
"implementation": "@metamask/recipe-runner/mobile/wallet"
|
|
2147
|
+
},
|
|
2070
2148
|
{
|
|
2071
2149
|
"action": "metamask.wallet.read_state",
|
|
2072
2150
|
"implementation": "@metamask/recipe-runner/mobile/wallet"
|
|
@@ -125,11 +125,18 @@
|
|
|
125
125
|
},
|
|
126
126
|
"wallet-read-state": {
|
|
127
127
|
"action": "metamask.wallet.read_state",
|
|
128
|
-
"next": "
|
|
128
|
+
"next": "wallet-list-accounts",
|
|
129
129
|
"intent": "Read wallet state for proof",
|
|
130
130
|
"detail": "Capture selected account and network state without mutating UI.",
|
|
131
131
|
"flow": "wallet"
|
|
132
132
|
},
|
|
133
|
+
"wallet-list-accounts": {
|
|
134
|
+
"action": "metamask.wallet.list_accounts",
|
|
135
|
+
"next": "extension-account-list",
|
|
136
|
+
"intent": "List wallet accounts for proof",
|
|
137
|
+
"detail": "Capture redacted account metadata and the selected account without mutating the app.",
|
|
138
|
+
"flow": "wallet"
|
|
139
|
+
},
|
|
133
140
|
"app-navigate-wallet": {
|
|
134
141
|
"action": "ui.navigate",
|
|
135
142
|
"next": "app-navigate-perps",
|
|
@@ -125,11 +125,18 @@
|
|
|
125
125
|
},
|
|
126
126
|
"wallet-read-state": {
|
|
127
127
|
"action": "metamask.wallet.read_state",
|
|
128
|
-
"next": "
|
|
128
|
+
"next": "wallet-list-accounts",
|
|
129
129
|
"intent": "Read wallet state for proof",
|
|
130
130
|
"detail": "Capture selected account and network state without mutating UI.",
|
|
131
131
|
"flow": "wallet"
|
|
132
132
|
},
|
|
133
|
+
"wallet-list-accounts": {
|
|
134
|
+
"action": "metamask.wallet.list_accounts",
|
|
135
|
+
"next": "select-account",
|
|
136
|
+
"intent": "List wallet accounts for proof",
|
|
137
|
+
"detail": "Capture redacted account metadata and the selected account without mutating the app.",
|
|
138
|
+
"flow": "wallet"
|
|
139
|
+
},
|
|
133
140
|
"select-account": {
|
|
134
141
|
"action": "metamask.wallet.select_account",
|
|
135
142
|
"address": "0x8dc623e964475d4d669da601fd15ea9125469003",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"title": "MetaMask Core runner smoke",
|
|
4
|
+
"description": "Proves the headless Core runner resolves the checkout, executes generic actions, and writes a valid artifact package.",
|
|
5
|
+
"validate": {
|
|
6
|
+
"workflow": {
|
|
7
|
+
"entry": "status",
|
|
8
|
+
"nodes": {
|
|
9
|
+
"status": {
|
|
10
|
+
"action": "app.status",
|
|
11
|
+
"next": "package",
|
|
12
|
+
"intent": "Resolve the Core checkout and report headless readiness"
|
|
13
|
+
},
|
|
14
|
+
"package": {
|
|
15
|
+
"action": "assert_file",
|
|
16
|
+
"path": "package.json",
|
|
17
|
+
"next": "done",
|
|
18
|
+
"intent": "Prove generic project-file assertions work in Core"
|
|
19
|
+
},
|
|
20
|
+
"done": {
|
|
21
|
+
"action": "end",
|
|
22
|
+
"status": "pass"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"title": "MetaMask Extension wallet smoke",
|
|
4
|
+
"description": "Fixture-backed proof that the Extension wallet is reachable, unlocked, readable, and visually capturable without depending on a product feature domain.",
|
|
5
|
+
"validate": {
|
|
6
|
+
"workflow": {
|
|
7
|
+
"entry": "status",
|
|
8
|
+
"nodes": {
|
|
9
|
+
"status": {
|
|
10
|
+
"action": "app.status",
|
|
11
|
+
"next": "fixture",
|
|
12
|
+
"intent": "Confirm the Extension runtime is reachable"
|
|
13
|
+
},
|
|
14
|
+
"fixture": {
|
|
15
|
+
"action": "metamask.wallet.fixture_status",
|
|
16
|
+
"next": "unlock",
|
|
17
|
+
"intent": "Confirm the checkout wallet fixture is ready"
|
|
18
|
+
},
|
|
19
|
+
"unlock": {
|
|
20
|
+
"action": "metamask.wallet.ensure_unlocked",
|
|
21
|
+
"next": "state",
|
|
22
|
+
"intent": "Ensure the fixture-backed wallet is unlocked"
|
|
23
|
+
},
|
|
24
|
+
"state": {
|
|
25
|
+
"action": "metamask.wallet.read_state",
|
|
26
|
+
"next": "screenshot",
|
|
27
|
+
"intent": "Read the current wallet state through the public adapter"
|
|
28
|
+
},
|
|
29
|
+
"screenshot": {
|
|
30
|
+
"action": "ui.screenshot",
|
|
31
|
+
"path": "screenshots/wallet-smoke.png",
|
|
32
|
+
"next": "done",
|
|
33
|
+
"intent": "Capture visual wallet evidence"
|
|
34
|
+
},
|
|
35
|
+
"done": {
|
|
36
|
+
"action": "end",
|
|
37
|
+
"status": "pass"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|