@deeeed/metamask-harness 0.34.4 → 0.36.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 +25 -0
- package/README.md +9 -1
- package/adapters/core/inject.sh +3 -3
- package/adapters/extension/check-infura-readiness.cjs +102 -0
- package/adapters/extension/inject.mjs +4 -4
- package/adapters/extension/live.sh +25 -10
- package/adapters/extension/start-watch.sh +15 -0
- package/adapters/extension/wallet-fixture-state.cjs +3 -1
- package/adapters/manifest.json +16 -0
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +73 -42
- package/adapters/mobile/inject.sh +4 -3
- package/adapters/mobile/reset-app-data.sh +154 -0
- package/adapters/mobile/verify.sh +37 -0
- package/dist/adapters/extension/product-config.js +29 -1
- package/dist/adapters/extension/runtime.js +60 -15
- package/dist/adapters/extension/surface.js +6 -1
- package/dist/adapters/mobile/surface.js +6 -1
- package/dist/adapters.js +18 -0
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +2 -2
- package/dist/command-contract.js +1 -1
- package/dist/commands/device-target.js +5 -0
- package/dist/commands/fixtures.js +106 -31
- package/dist/commands/launch/extension.js +39 -3
- package/dist/commands/launch/index.js +13 -0
- package/dist/doctor.js +30 -1
- package/dist/mm-harness-cli.js +6 -3
- package/dist/recipe-security.js +3 -0
- package/docs/RECIPES.md +29 -0
- package/library/actions/extension/wallet/import.mjs +234 -0
- package/library/actions/extension/wallet/reset.mjs +98 -0
- package/library/actions/extension/wallet/state.mjs +1 -0
- package/library/actions/mobile/analytics/consent-settings.mjs +112 -0
- package/library/actions/mobile/analytics/set_consent.mjs +4 -112
- package/library/actions/mobile/platform/bridge.mjs +8 -0
- package/library/actions/mobile/wallet/import.mjs +259 -0
- package/library/actions/mobile/wallet/reset-helper.mjs +99 -0
- package/library/actions/mobile/wallet/reset.mjs +7 -0
- package/library/actions/shared/wallet/import-source.mjs +101 -0
- package/library/manifests/extension.action-manifest.json +112 -0
- package/library/manifests/mobile.action-manifest.json +108 -0
- package/library/recipes/wallet/import.recipe.json +83 -0
- package/library/recipes/wallet/reset-import.recipe.json +88 -0
- package/package.json +1 -1
- package/scripts/completions.sh +2 -2
- package/scripts/site-contrast.mjs +7 -0
- package/site/architecture.html +14 -7
- package/site/assets/metamask-fox.svg +24 -0
- package/site/assets/progress.mjs +6 -0
- package/site/assets/style.css +88 -29
- package/site/cheatsheet.html +5 -6
- package/site/ecosystem.html +162 -0
- package/site/how-it-works.html +8 -9
- package/site/index.html +17 -12
- package/site/perps.html +103 -45
- package/site/recipes.html +5 -6
- package/site/reviewers.html +5 -6
- package/site/tutorials/index.html +5 -6
- package/site/tutorials/v1.html +5 -6
- package/site/tutorials/v2.html +6 -8
- package/site/tutorials/v3.html +5 -6
- package/site/tutorials/v4.html +5 -6
- package/site/tutorials/v5.html +5 -6
- package/site/tutorials/v6.html +5 -6
- package/site/tutorials/v7.html +5 -6
|
@@ -1237,6 +1237,45 @@
|
|
|
1237
1237
|
],
|
|
1238
1238
|
"execution_capabilities": []
|
|
1239
1239
|
},
|
|
1240
|
+
"metamask.wallet.validate_import": {
|
|
1241
|
+
"description": "Validate wallet recovery credentials and derive the expected primary address without launching or mutating the app.",
|
|
1242
|
+
"schema": {
|
|
1243
|
+
"type": "object",
|
|
1244
|
+
"properties": {
|
|
1245
|
+
"credential_source": {
|
|
1246
|
+
"type": "string",
|
|
1247
|
+
"enum": ["auto", "environment", "fixture"]
|
|
1248
|
+
},
|
|
1249
|
+
"srp_env": {
|
|
1250
|
+
"type": "string"
|
|
1251
|
+
},
|
|
1252
|
+
"password_env": {
|
|
1253
|
+
"type": "string"
|
|
1254
|
+
},
|
|
1255
|
+
"metametrics": {
|
|
1256
|
+
"type": "boolean"
|
|
1257
|
+
},
|
|
1258
|
+
"interests": {
|
|
1259
|
+
"type": "array",
|
|
1260
|
+
"items": {
|
|
1261
|
+
"type": "string"
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
"additionalProperties": false
|
|
1266
|
+
},
|
|
1267
|
+
"examples": [
|
|
1268
|
+
{
|
|
1269
|
+
"action": "metamask.wallet.validate_import",
|
|
1270
|
+
"credential_source": "auto",
|
|
1271
|
+
"intent": "Validate recovery credentials before resetting a wallet",
|
|
1272
|
+
"next": "done"
|
|
1273
|
+
}
|
|
1274
|
+
],
|
|
1275
|
+
"execution_capabilities": [
|
|
1276
|
+
"host-read-export"
|
|
1277
|
+
]
|
|
1278
|
+
},
|
|
1240
1279
|
"metamask.wallet.setup": {
|
|
1241
1280
|
"description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
|
|
1242
1281
|
"schema": {
|
|
@@ -1253,6 +1292,75 @@
|
|
|
1253
1292
|
],
|
|
1254
1293
|
"execution_capabilities": ["app-mutation"]
|
|
1255
1294
|
},
|
|
1295
|
+
"metamask.wallet.import": {
|
|
1296
|
+
"description": "Make the wallet ready by reusing a fixture-backed profile or importing the primary mnemonic through visible onboarding UI.",
|
|
1297
|
+
"schema": {
|
|
1298
|
+
"type": "object",
|
|
1299
|
+
"properties": {
|
|
1300
|
+
"method": {
|
|
1301
|
+
"type": "string",
|
|
1302
|
+
"enum": ["auto", "profile", "ui"]
|
|
1303
|
+
},
|
|
1304
|
+
"credential_source": {
|
|
1305
|
+
"type": "string",
|
|
1306
|
+
"enum": ["auto", "environment", "fixture"]
|
|
1307
|
+
},
|
|
1308
|
+
"srp_env": {
|
|
1309
|
+
"type": "string"
|
|
1310
|
+
},
|
|
1311
|
+
"password_env": {
|
|
1312
|
+
"type": "string"
|
|
1313
|
+
},
|
|
1314
|
+
"metametrics": {
|
|
1315
|
+
"type": "boolean",
|
|
1316
|
+
"default": false,
|
|
1317
|
+
"description": "Enable MetaMetrics during visible onboarding."
|
|
1318
|
+
},
|
|
1319
|
+
"interests": {
|
|
1320
|
+
"type": "array",
|
|
1321
|
+
"items": {
|
|
1322
|
+
"type": "string",
|
|
1323
|
+
"enum": ["swap_tokens", "trade_perpetuals", "prediction_markets", "send_receive_crypto", "earn_and_spend", "use_other_crypto_apps"]
|
|
1324
|
+
},
|
|
1325
|
+
"default": [],
|
|
1326
|
+
"description": "Select optional onboarding interests; an empty list skips the questionnaire."
|
|
1327
|
+
},
|
|
1328
|
+
"timeout_ms": {
|
|
1329
|
+
"type": "number"
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
"additionalProperties": false
|
|
1333
|
+
},
|
|
1334
|
+
"examples": [
|
|
1335
|
+
{
|
|
1336
|
+
"action": "metamask.wallet.import",
|
|
1337
|
+
"method": "auto",
|
|
1338
|
+
"intent": "Use an existing fixture profile or import through onboarding",
|
|
1339
|
+
"next": "done"
|
|
1340
|
+
}
|
|
1341
|
+
],
|
|
1342
|
+
"execution_capabilities": ["app-mutation"]
|
|
1343
|
+
},
|
|
1344
|
+
"metamask.wallet.reset": {
|
|
1345
|
+
"description": "Delete the current wallet through the visible client reset flow and reach fresh onboarding.",
|
|
1346
|
+
"schema": {
|
|
1347
|
+
"type": "object",
|
|
1348
|
+
"properties": {
|
|
1349
|
+
"timeout_ms": {
|
|
1350
|
+
"type": "number"
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
"additionalProperties": false
|
|
1354
|
+
},
|
|
1355
|
+
"examples": [
|
|
1356
|
+
{
|
|
1357
|
+
"action": "metamask.wallet.reset",
|
|
1358
|
+
"intent": "Reset the wallet through visible UI before importing it again",
|
|
1359
|
+
"next": "done"
|
|
1360
|
+
}
|
|
1361
|
+
],
|
|
1362
|
+
"execution_capabilities": ["app-mutation"]
|
|
1363
|
+
},
|
|
1256
1364
|
"metamask.wallet.ensure_unlocked": {
|
|
1257
1365
|
"description": "Unlock the wallet with the fixture password when it is currently locked.",
|
|
1258
1366
|
"schema": {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "Import or reuse a MetaMask wallet",
|
|
4
|
+
"description": "Make a Mobile or Extension wallet ready by reusing its fixture-backed profile or importing the primary mnemonic through visible onboarding UI.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"method": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["auto", "profile", "ui"],
|
|
12
|
+
"default": "auto",
|
|
13
|
+
"description": "Choose automatically from visible wallet state, require an existing profile, or require real-user onboarding UI."
|
|
14
|
+
},
|
|
15
|
+
"credential_source": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["auto", "environment", "fixture"],
|
|
18
|
+
"default": "auto",
|
|
19
|
+
"description": "Read secrets from the named environment variables first, or require one source explicitly."
|
|
20
|
+
},
|
|
21
|
+
"srp_env": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"default": "MM_HARNESS_WALLET_SRP",
|
|
24
|
+
"description": "Environment variable containing the Secret Recovery Phrase; the value is never stored in the recipe or evidence."
|
|
25
|
+
},
|
|
26
|
+
"password_env": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"default": "MM_HARNESS_WALLET_PASSWORD",
|
|
29
|
+
"description": "Environment variable containing the wallet password; the value is never stored in the recipe or evidence."
|
|
30
|
+
},
|
|
31
|
+
"metametrics": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"default": false,
|
|
34
|
+
"description": "Enable MetaMetrics during visible onboarding; defaults off."
|
|
35
|
+
},
|
|
36
|
+
"interests": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"enum": ["swap_tokens", "trade_perpetuals", "prediction_markets", "send_receive_crypto", "earn_and_spend", "use_other_crypto_apps"]
|
|
41
|
+
},
|
|
42
|
+
"default": [],
|
|
43
|
+
"description": "Mobile-only onboarding interests; an empty list skips the optional questionnaire."
|
|
44
|
+
},
|
|
45
|
+
"timeout_ms": {
|
|
46
|
+
"type": "number",
|
|
47
|
+
"default": 120000,
|
|
48
|
+
"description": "Maximum time for each onboarding transition and final wallet readiness."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"workflow": {
|
|
53
|
+
"entry": "fixture-status",
|
|
54
|
+
"nodes": {
|
|
55
|
+
"fixture-status": {
|
|
56
|
+
"action": "metamask.wallet.fixture_status",
|
|
57
|
+
"intent": "Record whether a canonical fixture is available without exposing its secrets",
|
|
58
|
+
"next": "import"
|
|
59
|
+
},
|
|
60
|
+
"import": {
|
|
61
|
+
"action": "metamask.wallet.import",
|
|
62
|
+
"method": "{{params.method}}",
|
|
63
|
+
"credential_source": "{{params.credential_source}}",
|
|
64
|
+
"srp_env": "{{params.srp_env}}",
|
|
65
|
+
"password_env": "{{params.password_env}}",
|
|
66
|
+
"metametrics": "{{params.metametrics}}",
|
|
67
|
+
"interests": "{{params.interests}}",
|
|
68
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
69
|
+
"intent": "Reuse the fixture-backed wallet or import it through the visible onboarding flow",
|
|
70
|
+
"next": "state"
|
|
71
|
+
},
|
|
72
|
+
"state": {
|
|
73
|
+
"action": "metamask.wallet.read_state",
|
|
74
|
+
"intent": "Prove the wallet has a selected account after setup",
|
|
75
|
+
"next": "done"
|
|
76
|
+
},
|
|
77
|
+
"done": {
|
|
78
|
+
"action": "end",
|
|
79
|
+
"status": "pass"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
|
|
3
|
+
"title": "Reset and import a MetaMask wallet through visible UI",
|
|
4
|
+
"description": "Delete the current Mobile or Extension wallet through its visible reset flow, import the primary mnemonic through onboarding, and prove a selected account is ready.",
|
|
5
|
+
"paramsSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"credential_source": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["auto", "environment", "fixture"],
|
|
12
|
+
"default": "auto",
|
|
13
|
+
"description": "Read secrets from the named environment variables first, or require one source explicitly."
|
|
14
|
+
},
|
|
15
|
+
"srp_env": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"default": "MM_HARNESS_WALLET_SRP",
|
|
18
|
+
"description": "Environment variable containing the Secret Recovery Phrase; the value is never stored in the recipe or evidence."
|
|
19
|
+
},
|
|
20
|
+
"password_env": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"default": "MM_HARNESS_WALLET_PASSWORD",
|
|
23
|
+
"description": "Environment variable containing the wallet password; the value is never stored in the recipe or evidence."
|
|
24
|
+
},
|
|
25
|
+
"metametrics": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false,
|
|
28
|
+
"description": "Enable MetaMetrics during visible onboarding; defaults off."
|
|
29
|
+
},
|
|
30
|
+
"interests": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"enum": ["swap_tokens", "trade_perpetuals", "prediction_markets", "send_receive_crypto", "earn_and_spend", "use_other_crypto_apps"]
|
|
35
|
+
},
|
|
36
|
+
"default": [],
|
|
37
|
+
"description": "Mobile-only onboarding interests; an empty list skips the optional questionnaire."
|
|
38
|
+
},
|
|
39
|
+
"timeout_ms": {
|
|
40
|
+
"type": "number",
|
|
41
|
+
"default": 120000,
|
|
42
|
+
"description": "Maximum time for each reset/import transition and final wallet readiness."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"workflow": {
|
|
47
|
+
"entry": "validate_import",
|
|
48
|
+
"nodes": {
|
|
49
|
+
"validate_import": {
|
|
50
|
+
"action": "metamask.wallet.validate_import",
|
|
51
|
+
"credential_source": "{{params.credential_source}}",
|
|
52
|
+
"srp_env": "{{params.srp_env}}",
|
|
53
|
+
"password_env": "{{params.password_env}}",
|
|
54
|
+
"metametrics": "{{params.metametrics}}",
|
|
55
|
+
"interests": "{{params.interests}}",
|
|
56
|
+
"intent": "Validate recovery credentials before deleting the current wallet",
|
|
57
|
+
"next": "reset"
|
|
58
|
+
},
|
|
59
|
+
"reset": {
|
|
60
|
+
"action": "metamask.wallet.reset",
|
|
61
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
62
|
+
"intent": "Delete the current wallet through the client-visible reset flow and reach fresh onboarding",
|
|
63
|
+
"next": "import"
|
|
64
|
+
},
|
|
65
|
+
"import": {
|
|
66
|
+
"action": "metamask.wallet.import",
|
|
67
|
+
"method": "ui",
|
|
68
|
+
"credential_source": "{{params.credential_source}}",
|
|
69
|
+
"srp_env": "{{params.srp_env}}",
|
|
70
|
+
"password_env": "{{params.password_env}}",
|
|
71
|
+
"metametrics": "{{params.metametrics}}",
|
|
72
|
+
"interests": "{{params.interests}}",
|
|
73
|
+
"timeout_ms": "{{params.timeout_ms}}",
|
|
74
|
+
"intent": "Import the primary mnemonic through visible onboarding UI",
|
|
75
|
+
"next": "state"
|
|
76
|
+
},
|
|
77
|
+
"state": {
|
|
78
|
+
"action": "metamask.wallet.read_state",
|
|
79
|
+
"intent": "Prove the imported wallet has a selected account",
|
|
80
|
+
"next": "done"
|
|
81
|
+
},
|
|
82
|
+
"done": {
|
|
83
|
+
"action": "end",
|
|
84
|
+
"status": "pass"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/package.json
CHANGED
package/scripts/completions.sh
CHANGED
|
@@ -77,7 +77,7 @@ if [ -n "${BASH_VERSION:-}" ]; then
|
|
|
77
77
|
|
|
78
78
|
case "$cmd" in
|
|
79
79
|
launch) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "ios android" -- "$cur")) ;;
|
|
80
|
-
fixtures) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "init sync set generate finalize" -- "$cur")) ;;
|
|
80
|
+
fixtures) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "init sync set reset generate finalize" -- "$cur")) ;;
|
|
81
81
|
checklist) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "mark closeout" -- "$cur")) ;;
|
|
82
82
|
completions) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "install bash zsh status" -- "$cur")) ;;
|
|
83
83
|
call) [ "$cword" -eq 2 ] && COMPREPLY=($(compgen -W "$(_mmh_actions)" -- "$cur")) ;;
|
|
@@ -111,7 +111,7 @@ if [ -n "${ZSH_VERSION:-}" ]; then
|
|
|
111
111
|
|
|
112
112
|
case "$cmd" in
|
|
113
113
|
launch) (( CURRENT == 3 )) && { candidates=(ios android); compadd -a candidates; } ;;
|
|
114
|
-
fixtures) (( CURRENT == 3 )) && { candidates=(init sync set generate finalize); compadd -a candidates; } ;;
|
|
114
|
+
fixtures) (( CURRENT == 3 )) && { candidates=(init sync set reset generate finalize); compadd -a candidates; } ;;
|
|
115
115
|
checklist) (( CURRENT == 3 )) && { candidates=(mark closeout); compadd -a candidates; } ;;
|
|
116
116
|
completions) (( CURRENT == 3 )) && { candidates=(install bash zsh status); compadd -a candidates; } ;;
|
|
117
117
|
call) (( CURRENT == 3 )) && { candidates=("${(@f)$(_mmh_actions)}"); compadd -a candidates; } ;;
|
|
@@ -358,6 +358,13 @@ async function checkBehavior(browserEndpoint, port, pages) {
|
|
|
358
358
|
tab.click();
|
|
359
359
|
const panel = document.getElementById(tab.getAttribute('aria-controls'));
|
|
360
360
|
if (!panel || panel.hidden) throw new Error('tab ' + tab.id + ' did not reveal its panel');
|
|
361
|
+
// Panels ship visible so a dead module degrades to readable prompts
|
|
362
|
+
// rather than dead controls — which also means "revealed" alone
|
|
363
|
+
// proves nothing. Selecting one has to hide the others.
|
|
364
|
+
const shown = [...document.querySelectorAll('[role="tabpanel"]')].filter((p) => !p.hidden);
|
|
365
|
+
if (shown.length !== 1) {
|
|
366
|
+
throw new Error('tab ' + tab.id + ' left ' + shown.length + ' panels visible');
|
|
367
|
+
}
|
|
361
368
|
out.work.push(await grab(panel.querySelector('.cmd .copy')));
|
|
362
369
|
}
|
|
363
370
|
return out;
|
package/site/architecture.html
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Architecture — how the pieces fit</title>
|
|
7
7
|
<meta name="description" content="Skills, mm-harness, team recipe libraries, product repos: what lives where, the three workflows, and the mistakes newcomers make.">
|
|
8
|
-
<link rel="
|
|
8
|
+
<link rel="icon" href="assets/metamask-fox.svg" type="image/svg+xml">
|
|
9
|
+
<link rel="stylesheet" href="assets/style.css?v=4">
|
|
9
10
|
</head>
|
|
10
11
|
<body data-progress-page="architecture">
|
|
11
12
|
<a class="skip" href="#stack">Skip to the stack map</a>
|
|
@@ -13,18 +14,16 @@
|
|
|
13
14
|
<header class="topbar">
|
|
14
15
|
<div class="wrap topbar-inner">
|
|
15
16
|
<a class="brand" href="index.html">
|
|
16
|
-
<
|
|
17
|
+
<img class="brand-mark" src="assets/metamask-fox.svg" alt="" width="22" height="22">
|
|
17
18
|
<span class="brand-name">recipes</span>
|
|
18
19
|
</a>
|
|
19
20
|
<nav class="nav" aria-label="Main">
|
|
20
21
|
<a class="nav-cta" href="index.html#prompt">Quick start</a>
|
|
21
22
|
<a href="how-it-works.html">How it works</a>
|
|
22
|
-
<a href="
|
|
23
|
-
<a href="perps.html">Perps</a>
|
|
23
|
+
<a href="perps.html">Team / Perps</a>
|
|
24
24
|
<a href="cheatsheet.html">Cheatsheet</a>
|
|
25
25
|
<a href="architecture.html" aria-current="page">Architecture</a>
|
|
26
26
|
<a href="tutorials/index.html">Tutorials</a>
|
|
27
|
-
<a href="reviewers.html">For Reviewers</a>
|
|
28
27
|
</nav>
|
|
29
28
|
</div>
|
|
30
29
|
</header>
|
|
@@ -471,8 +470,16 @@
|
|
|
471
470
|
</ol>
|
|
472
471
|
|
|
473
472
|
<hr class="sep">
|
|
473
|
+
<h2>Which repo is which</h2>
|
|
474
|
+
<p>
|
|
475
|
+
The layers above are the design. The <a href="ecosystem.html">Ecosystem map</a> is the concrete
|
|
476
|
+
version: the six repositories and packages behind a run, each with its URL, and which way the
|
|
477
|
+
dependencies point.
|
|
478
|
+
</p>
|
|
479
|
+
|
|
474
480
|
<div class="btn-row">
|
|
475
|
-
<a class="btn btn-primary" href="
|
|
481
|
+
<a class="btn btn-primary" href="ecosystem.html">See the repo map →</a>
|
|
482
|
+
<a class="btn btn-ghost" href="how-it-works.html#steps">Do the walkthrough</a>
|
|
476
483
|
<a class="btn btn-ghost" href="reviewers.html">Read an evidence bundle</a>
|
|
477
484
|
</div>
|
|
478
485
|
</section>
|
|
@@ -485,6 +492,6 @@
|
|
|
485
492
|
</div>
|
|
486
493
|
</footer>
|
|
487
494
|
|
|
488
|
-
<script type="module" src="assets/progress.mjs"></script>
|
|
495
|
+
<script type="module" src="assets/progress.mjs?v=4"></script>
|
|
489
496
|
</body>
|
|
490
497
|
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg width="35" height="34" viewBox="0 0 35 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M32.7077 32.7522L25.1688 30.5174L19.4833 33.9008L15.5167 33.8991L9.82793 30.5174L2.29225 32.7522L0 25.0489L2.29225 16.4993L0 9.27094L2.29225 0.312256L14.0674 7.31554H20.9326L32.7077 0.312256L35 9.27094L32.7077 16.4993L35 25.0489L32.7077 32.7522Z" fill="#FF5C16"/>
|
|
3
|
+
<path d="M2.29395 0.312256L14.0691 7.32047L13.6008 12.1301L2.29395 0.312256Z" fill="#FF5C16"/>
|
|
4
|
+
<path d="M9.82959 25.0522L15.0106 28.9811L9.82959 30.5175V25.0522Z" fill="#FF5C16"/>
|
|
5
|
+
<path d="M14.5966 18.5565L13.6009 12.1333L7.22692 16.5009L7.22363 16.4993V16.5025L7.24335 20.9983L9.82809 18.5565H9.82974H14.5966Z" fill="#FF5C16"/>
|
|
6
|
+
<path d="M32.7077 0.312256L20.9326 7.32047L21.3993 12.1301L32.7077 0.312256Z" fill="#FF5C16"/>
|
|
7
|
+
<path d="M25.1722 25.0522L19.9912 28.9811L25.1722 30.5175V25.0522Z" fill="#FF5C16"/>
|
|
8
|
+
<path d="M27.7766 16.5025H27.7783H27.7766V16.4993L27.775 16.5009L21.401 12.1333L20.4053 18.5565H25.1722L27.7586 20.9983L27.7766 16.5025Z" fill="#FF5C16"/>
|
|
9
|
+
<path d="M9.82793 30.5175L2.29225 32.7522L0 25.0522H9.82793V30.5175Z" fill="#E34807"/>
|
|
10
|
+
<path d="M14.5947 18.5549L16.0341 27.8406L14.0393 22.6777L7.23975 20.9984L9.82613 18.5549H14.593H14.5947Z" fill="#E34807"/>
|
|
11
|
+
<path d="M25.1721 30.5175L32.7078 32.7522L35.0001 25.0522H25.1721V30.5175Z" fill="#E34807"/>
|
|
12
|
+
<path d="M20.4053 18.5549L18.9658 27.8406L20.9607 22.6777L27.7602 20.9984L25.1722 18.5549H20.4053Z" fill="#E34807"/>
|
|
13
|
+
<path d="M0 25.0488L2.29225 16.4993H7.22183L7.23991 20.9967L14.0394 22.676L16.0343 27.8389L15.0089 28.976L9.82793 25.0472H0V25.0488Z" fill="#FF8D5D"/>
|
|
14
|
+
<path d="M35.0001 25.0488L32.7078 16.4993H27.7783L27.7602 20.9967L20.9607 22.676L18.9658 27.8389L19.9912 28.976L25.1722 25.0472H35.0001V25.0488Z" fill="#FF8D5D"/>
|
|
15
|
+
<path d="M20.9325 7.31543H17.4999H14.0673L13.6006 12.1251L16.0342 27.834H18.9656L21.4008 12.1251L20.9325 7.31543Z" fill="#FF8D5D"/>
|
|
16
|
+
<path d="M2.29225 0.312256L0 9.27094L2.29225 16.4993H7.22183L13.5991 12.1301L2.29225 0.312256Z" fill="#661800"/>
|
|
17
|
+
<path d="M13.17 20.4199H10.9369L9.72095 21.6062L14.0409 22.6727L13.17 20.4182V20.4199Z" fill="#661800"/>
|
|
18
|
+
<path d="M32.7077 0.312256L34.9999 9.27094L32.7077 16.4993H27.7781L21.4009 12.1301L32.7077 0.312256Z" fill="#661800"/>
|
|
19
|
+
<path d="M21.833 20.4199H24.0694L25.2853 21.6079L20.9604 22.676L21.833 20.4182V20.4199Z" fill="#661800"/>
|
|
20
|
+
<path d="M19.4817 30.8362L19.9911 28.9794L18.9658 27.8423H16.0327L15.0073 28.9794L15.5167 30.8362" fill="#661800"/>
|
|
21
|
+
<path d="M19.4816 30.8359V33.9021H15.5166V30.8359H19.4816Z" fill="#C0C4CD"/>
|
|
22
|
+
<path d="M9.82959 30.5142L15.52 33.9008V30.8346L15.0106 28.9778L9.82959 30.5142Z" fill="#E7EBF6"/>
|
|
23
|
+
<path d="M25.1721 30.5142L19.4817 33.9008V30.8346L19.9911 28.9778L25.1721 30.5142Z" fill="#E7EBF6"/>
|
|
24
|
+
</svg>
|
package/site/assets/progress.mjs
CHANGED
|
@@ -241,11 +241,17 @@
|
|
|
241
241
|
* One tablist per group, roving tabindex: only the selected tab is in the
|
|
242
242
|
* tab order, arrows move between them. Panels stay in the DOM so their
|
|
243
243
|
* commands remain copyable the moment a tab is shown.
|
|
244
|
+
*
|
|
245
|
+
* The markup ships every panel visible and the tablist hidden; hiding is
|
|
246
|
+
* this function's first act. Without it — stale cache, blocked module,
|
|
247
|
+
* file:// — the reader gets all three prompts stacked instead of controls
|
|
248
|
+
* that do nothing.
|
|
244
249
|
*/
|
|
245
250
|
function initTabs() {
|
|
246
251
|
[].slice.call(document.querySelectorAll('[role="tablist"]')).forEach(function (list) {
|
|
247
252
|
var tabs = [].slice.call(list.querySelectorAll('[role="tab"]'));
|
|
248
253
|
if (!tabs.length) return;
|
|
254
|
+
list.hidden = false;
|
|
249
255
|
|
|
250
256
|
function select(tab, focus) {
|
|
251
257
|
tabs.forEach(function (other) {
|
package/site/assets/style.css
CHANGED
|
@@ -110,8 +110,10 @@ main { padding-bottom: 6rem; }
|
|
|
110
110
|
.topbar-inner {
|
|
111
111
|
display: flex;
|
|
112
112
|
align-items: center;
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
gap: 0.5rem 1.5rem;
|
|
115
|
+
min-height: 60px;
|
|
116
|
+
padding: 0.5rem 0;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
.brand {
|
|
@@ -125,31 +127,24 @@ main { padding-bottom: 6rem; }
|
|
|
125
127
|
}
|
|
126
128
|
.brand:hover { text-decoration: none; }
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
.brand-mark {
|
|
130
|
-
width: 20px;
|
|
131
|
-
height: 20px;
|
|
132
|
-
flex: none;
|
|
133
|
-
border-radius: 5px;
|
|
134
|
-
background: linear-gradient(145deg, var(--accent) 0%, var(--accent-deep) 100%);
|
|
135
|
-
transform: rotate(45deg);
|
|
136
|
-
box-shadow: 0 0 0 1px rgba(246, 133, 27, 0.3), 0 3px 12px rgba(246, 133, 27, 0.22);
|
|
137
|
-
}
|
|
130
|
+
.brand-mark { width: 22px; height: 22px; flex: none; display: block; }
|
|
138
131
|
|
|
139
132
|
.brand-name { font-family: var(--mono); font-size: 0.95rem; }
|
|
140
133
|
|
|
134
|
+
/* Six items fit a laptop on one line; below that they wrap onto a second row
|
|
135
|
+
rather than scrolling a label out of sight. */
|
|
141
136
|
.nav {
|
|
142
137
|
display: flex;
|
|
143
138
|
gap: 0.3rem;
|
|
144
139
|
margin-left: auto;
|
|
145
140
|
min-width: 0;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
scrollbar-width: thin;
|
|
141
|
+
flex-wrap: wrap;
|
|
142
|
+
justify-content: flex-end;
|
|
149
143
|
}
|
|
150
144
|
|
|
151
145
|
.nav a {
|
|
152
146
|
flex: none;
|
|
147
|
+
white-space: nowrap;
|
|
153
148
|
color: var(--text-dim);
|
|
154
149
|
font-size: 0.885rem;
|
|
155
150
|
font-weight: 500;
|
|
@@ -272,16 +267,7 @@ main { padding-bottom: 6rem; }
|
|
|
272
267
|
text-align: center;
|
|
273
268
|
}
|
|
274
269
|
|
|
275
|
-
.lobby-mark {
|
|
276
|
-
display: block;
|
|
277
|
-
width: 26px;
|
|
278
|
-
height: 26px;
|
|
279
|
-
margin: 0 auto 2rem;
|
|
280
|
-
border-radius: 7px;
|
|
281
|
-
background: linear-gradient(145deg, var(--accent) 0%, var(--accent-deep) 100%);
|
|
282
|
-
transform: rotate(45deg);
|
|
283
|
-
box-shadow: 0 0 0 1px rgba(246, 133, 27, 0.3), 0 4px 18px rgba(246, 133, 27, 0.25);
|
|
284
|
-
}
|
|
270
|
+
.lobby-mark { display: block; width: 52px; height: 52px; margin: 0 auto 1.6rem; }
|
|
285
271
|
|
|
286
272
|
.lobby h1 {
|
|
287
273
|
font-size: clamp(2rem, 4.6vw, 2.85rem);
|
|
@@ -352,17 +338,19 @@ main { padding-bottom: 6rem; }
|
|
|
352
338
|
.qs-title {
|
|
353
339
|
display: flex;
|
|
354
340
|
align-items: center;
|
|
355
|
-
gap: 0.65rem;
|
|
356
341
|
font-size: 1.16rem;
|
|
357
342
|
font-weight: 640;
|
|
358
343
|
margin: 0 0 0.35rem;
|
|
359
344
|
}
|
|
360
345
|
|
|
346
|
+
/* The margin rather than a flex gap: the badge stays separated from the title
|
|
347
|
+
even when the flex layout does not apply. */
|
|
361
348
|
.qs-num {
|
|
362
349
|
flex: none;
|
|
363
|
-
width: 1.
|
|
364
|
-
height: 1.
|
|
365
|
-
|
|
350
|
+
width: 1.7rem;
|
|
351
|
+
height: 1.7rem;
|
|
352
|
+
margin-right: 0.75rem;
|
|
353
|
+
display: inline-grid;
|
|
366
354
|
place-items: center;
|
|
367
355
|
border-radius: 50%;
|
|
368
356
|
background: var(--accent-soft);
|
|
@@ -370,6 +358,7 @@ main { padding-bottom: 6rem; }
|
|
|
370
358
|
color: var(--accent);
|
|
371
359
|
font-family: var(--mono);
|
|
372
360
|
font-size: 0.8rem;
|
|
361
|
+
line-height: 1;
|
|
373
362
|
}
|
|
374
363
|
|
|
375
364
|
.qs-why { color: var(--text-dim); font-size: 0.95rem; margin-bottom: 0.9rem; }
|
|
@@ -960,6 +949,76 @@ tr[hidden], .filter-hidden { display: none !important; }
|
|
|
960
949
|
.verdict.pass { color: var(--pass); }
|
|
961
950
|
.verdict.fail { color: var(--fail); }
|
|
962
951
|
|
|
952
|
+
/* ---------- dependency map ----------
|
|
953
|
+
A column of nodes with the edge label between them, so the direction reads
|
|
954
|
+
top to bottom without a diagram library. */
|
|
955
|
+
|
|
956
|
+
.map { margin: 1.5rem 0 2.5rem; }
|
|
957
|
+
|
|
958
|
+
.node {
|
|
959
|
+
background: linear-gradient(180deg, var(--surface) 0%, var(--bg-raised) 100%);
|
|
960
|
+
border: 1px solid var(--line);
|
|
961
|
+
border-radius: var(--radius);
|
|
962
|
+
padding: 1.25rem 1.4rem;
|
|
963
|
+
}
|
|
964
|
+
.node:hover { border-color: var(--accent-line); }
|
|
965
|
+
|
|
966
|
+
/* The one you install. */
|
|
967
|
+
.node-key { border-color: var(--accent-line); }
|
|
968
|
+
|
|
969
|
+
.node-tag {
|
|
970
|
+
display: block;
|
|
971
|
+
font-family: var(--mono);
|
|
972
|
+
font-size: 0.72rem;
|
|
973
|
+
letter-spacing: 0.08em;
|
|
974
|
+
text-transform: uppercase;
|
|
975
|
+
color: var(--text-faint);
|
|
976
|
+
margin-bottom: 0.35rem;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.node-name { font-size: 1.12rem; font-weight: 640; margin: 0 0 0.4rem; }
|
|
980
|
+
.node-what { font-size: 0.94rem; color: var(--text-dim); margin-bottom: 0.7rem; }
|
|
981
|
+
|
|
982
|
+
.node-list {
|
|
983
|
+
list-style: none;
|
|
984
|
+
margin: 0 0 0.8rem;
|
|
985
|
+
padding: 0;
|
|
986
|
+
display: flex;
|
|
987
|
+
flex-wrap: wrap;
|
|
988
|
+
gap: 0.35rem 0.5rem;
|
|
989
|
+
font-size: 0.86rem;
|
|
990
|
+
}
|
|
991
|
+
.node-list li { color: var(--text-dim); }
|
|
992
|
+
|
|
993
|
+
.node-url {
|
|
994
|
+
display: block;
|
|
995
|
+
font-family: var(--mono);
|
|
996
|
+
font-size: 0.8rem;
|
|
997
|
+
word-break: break-all;
|
|
998
|
+
margin-bottom: 0.15rem;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.node .badge { margin: 0.55rem 0.4rem 0 0; display: inline-block; }
|
|
1002
|
+
.node-more { display: block; font-size: 0.88rem; margin-top: 0.6rem; }
|
|
1003
|
+
|
|
1004
|
+
/* Edge labels carry the verb; the arrow alone would not say what flows. */
|
|
1005
|
+
.map-edge {
|
|
1006
|
+
margin: 0.7rem 0;
|
|
1007
|
+
text-align: center;
|
|
1008
|
+
font-family: var(--mono);
|
|
1009
|
+
font-size: 0.8rem;
|
|
1010
|
+
color: var(--text-faint);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.map-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
|
|
1014
|
+
.map-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; align-items: start; }
|
|
1015
|
+
|
|
1016
|
+
@media (max-width: 780px) {
|
|
1017
|
+
.map-split { grid-template-columns: 1fr; }
|
|
1018
|
+
.map-split .map-edge:last-child { margin-top: 0; }
|
|
1019
|
+
.map-row { grid-template-columns: 1fr; }
|
|
1020
|
+
}
|
|
1021
|
+
|
|
963
1022
|
/* ---------- misc ---------- */
|
|
964
1023
|
|
|
965
1024
|
.btn-row { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.8rem; }
|
package/site/cheatsheet.html
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Cheatsheet — mm-harness</title>
|
|
7
7
|
<meta name="description" content="Every mm-harness command worth knowing, filterable by platform, with copy buttons.">
|
|
8
|
-
<link rel="
|
|
8
|
+
<link rel="icon" href="assets/metamask-fox.svg" type="image/svg+xml">
|
|
9
|
+
<link rel="stylesheet" href="assets/style.css?v=4">
|
|
9
10
|
</head>
|
|
10
11
|
<body data-progress-page="cheatsheet">
|
|
11
12
|
<a class="skip" href="#daily">Skip to the commands</a>
|
|
@@ -13,18 +14,16 @@
|
|
|
13
14
|
<header class="topbar">
|
|
14
15
|
<div class="wrap-wide topbar-inner">
|
|
15
16
|
<a class="brand" href="index.html">
|
|
16
|
-
<
|
|
17
|
+
<img class="brand-mark" src="assets/metamask-fox.svg" alt="" width="22" height="22">
|
|
17
18
|
<span class="brand-name">recipes</span>
|
|
18
19
|
</a>
|
|
19
20
|
<nav class="nav" aria-label="Main">
|
|
20
21
|
<a class="nav-cta" href="index.html#prompt">Quick start</a>
|
|
21
22
|
<a href="how-it-works.html">How it works</a>
|
|
22
|
-
<a href="
|
|
23
|
-
<a href="perps.html">Perps</a>
|
|
23
|
+
<a href="perps.html">Team / Perps</a>
|
|
24
24
|
<a href="cheatsheet.html" aria-current="page">Cheatsheet</a>
|
|
25
25
|
<a href="architecture.html">Architecture</a>
|
|
26
26
|
<a href="tutorials/index.html">Tutorials</a>
|
|
27
|
-
<a href="reviewers.html">For Reviewers</a>
|
|
28
27
|
</nav>
|
|
29
28
|
</div>
|
|
30
29
|
</header>
|
|
@@ -303,6 +302,6 @@
|
|
|
303
302
|
</div>
|
|
304
303
|
</footer>
|
|
305
304
|
|
|
306
|
-
<script type="module" src="assets/progress.mjs"></script>
|
|
305
|
+
<script type="module" src="assets/progress.mjs?v=4"></script>
|
|
307
306
|
</body>
|
|
308
307
|
</html>
|