@deeeed/metamask-harness 0.80.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/adapters/manifest.json +14 -30
  3. package/adapters/terminal/launch.mjs +17 -13
  4. package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
  5. package/adapters/terminal/lib/readiness.mjs +20 -12
  6. package/adapters/terminal/policy.mjs +35 -0
  7. package/adapters/terminal/verify.mjs +10 -3
  8. package/adapters/terminal/wallet-host.mjs +43 -225
  9. package/dist/adapters/core/actions.js +4 -1
  10. package/dist/adapters/extension/actions.js +6 -2
  11. package/dist/adapters/extension/network-observer.js +13 -0
  12. package/dist/adapters/extension/run.js +112 -5
  13. package/dist/adapters/mobile/actions.js +4 -1
  14. package/dist/adapters/mobile/network-observer.js +68 -0
  15. package/dist/adapters/mobile/performance-observer.js +1 -3
  16. package/dist/adapters/mobile/run.js +32 -9
  17. package/dist/adapters/perps-order-inputs.js +36 -0
  18. package/dist/adapters/proof-failure.js +11 -0
  19. package/dist/adapters/terminal/actions.js +4 -1
  20. package/dist/adapters/terminal/doctor-checks.js +2 -0
  21. package/dist/adapters/terminal/launch.js +3 -0
  22. package/dist/adapters/terminal/overlay.js +8 -1
  23. package/dist/adapters/terminal/run.js +8 -3
  24. package/dist/adapters/terminal/surface.js +2 -0
  25. package/dist/adapters.js +20 -4
  26. package/dist/cli.js +31 -6
  27. package/dist/commands/check.js +30 -6
  28. package/dist/commands/completion-candidates.js +5 -5
  29. package/dist/commands/self-test.js +4 -3
  30. package/dist/engine.js +57 -0
  31. package/dist/funding-execution-context.js +81 -0
  32. package/dist/mm-harness-cli.js +3 -3
  33. package/dist/performance-observation.js +1 -3
  34. package/dist/recipe-assistance-context.js +6 -7
  35. package/dist/runner.js +2 -1
  36. package/docs/CONTRIBUTING.md +4 -2
  37. package/docs/RECIPES.md +31 -3
  38. package/library/actions/core/perps/_controller.mjs +24 -28
  39. package/library/actions/core/perps/_rewards.mjs +186 -0
  40. package/library/actions/core/perps/close_positions.mjs +1 -1
  41. package/library/actions/core/perps/place_order.mjs +167 -45
  42. package/library/actions/core/perps/quote_fees.mjs +7 -4
  43. package/library/actions/core/wallet/list_accounts.mjs +1 -1
  44. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  45. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  46. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  47. package/library/actions/extension/perps/perps.mjs +320 -3
  48. package/library/actions/extension/perps/place_order.mjs +3 -1
  49. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  50. package/library/actions/extension/platform/cdp.mjs +71 -3
  51. package/library/actions/mobile/perps/perps.mjs +216 -11
  52. package/library/actions/mobile/perps/place_order.mjs +3 -1
  53. package/library/actions/shared/perps/order-policy.mjs +177 -0
  54. package/library/actions/{core/wallet/_fixture-accounts.mjs → shared/wallet/fixture-accounts.mjs} +18 -3
  55. package/library/actions/terminal/app/launch.mjs +3 -2
  56. package/library/actions/terminal/platform/hud.mjs +2 -1
  57. package/library/actions/terminal/platform/page.mjs +2 -1
  58. package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
  59. package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
  60. package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
  61. package/library/manifests/core.action-manifest.json +6 -2
  62. package/library/manifests/extension.action-manifest.json +4 -0
  63. package/library/manifests/mobile.action-manifest.json +12 -0
  64. package/package.json +4 -4
  65. package/site/assets/help-recipes.json +1 -0
  66. package/adapters/terminal/lib/origin.mjs +0 -30
  67. package/adapters/terminal/lib/page-script.mjs +0 -246
  68. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  69. package/dist/commands/call.js +0 -671
  70. package/dist/commands/list-executables.js +0 -245
  71. package/dist/commands/manifest.js +0 -477
  72. package/dist/commands/run-engine.js +0 -1301
  73. package/dist/commands/run.js +0 -730
  74. package/dist/discovery.js +0 -15
  75. package/dist/metamask-action-validation.js +0 -68
  76. package/dist/network-observation.js +0 -290
@@ -1,245 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { resolveActionManifest } from "../manifest.js";
4
- import { importRecipeProtocol } from "../paths.js";
5
- import { color, EXIT, optionFlag, optionString, optionStrings, resolveAdapter, shellQuoteArg } from "@farmslot/recipe-cli/harness";
6
- import { actionDomain, describeManifestActions, renderHumanActionCatalog } from "./manifest.js";
7
- import { shortActionNames } from "@farmslot/recipe-cli";
8
- import {
9
- describeRunnableRecipe,
10
- listRunnableRecipes,
11
- resolveMetaMaskLibrarySources
12
- } from "./run-engine.js";
13
- const recipeDomain = (name) => name.includes(".") ? name.split(".")[0].replaceAll("_", "-") : "general";
14
- function catalogDescription(description) {
15
- if (!description) return "";
16
- const firstSentence = description.match(/^.*?[.!?](?:\s|$)/u)?.[0]?.trim() ?? description;
17
- return firstSentence.length <= 180 ? firstSentence : `${firstSentence.slice(0, 177).trimEnd()}\u2026`;
18
- }
19
- async function handleListExecutables(command, options) {
20
- const json = optionFlag(options, "json");
21
- const { adapter } = resolveAdapter(options);
22
- const domainFilter = optionString(options, "domain")?.replaceAll("_", "-");
23
- const sourceFilter = optionString(options, "source");
24
- const sort = optionString(options, "sort") ?? (json ? "name" : "domain");
25
- const groupKey = (entry, domain) => sort === "library" ? entry.source : sort === "domain" ? domain : "";
26
- const select = (available2, domainOf) => available2.filter((entry) => (!domainFilter || domainOf(entry) === domainFilter) && (!sourceFilter || entry.source === sourceFilter)).sort((left, right) => groupKey(left, domainOf(left)).localeCompare(groupKey(right, domainOf(right))) || left.name.localeCompare(right.name));
27
- const out = (style, text) => color(style, text, { stream: process.stdout });
28
- const stylesFor = (domains) => {
29
- const palette = ["label", "accent", "ok", "warn"];
30
- return new Map([...new Set(domains)].sort().map((domain, index) => [domain, palette[index % palette.length]]));
31
- };
32
- const librarySources = await resolveMetaMaskLibrarySources(optionStrings(options, "library"));
33
- const libraryLines = () => {
34
- const lines = ["Libraries loaded:"];
35
- for (const source of librarySources ?? []) {
36
- const name = source.name ?? path.basename(source.root);
37
- const kind = source.provenance?.kind === "bundled" ? "bundled" : "linked";
38
- lines.push(` ${out("bold", name)} ${kind} \xB7 ${source.root}`);
39
- }
40
- if (domainFilter || sourceFilter) {
41
- lines.push(`Filter: ${[domainFilter && `domain=${domainFilter}`, sourceFilter && `source=${sourceFilter}`].filter(Boolean).join(" ")}`);
42
- }
43
- return lines;
44
- };
45
- if (command === "run") {
46
- const available2 = await listRunnableRecipes(adapter, librarySources);
47
- const recipes = select(available2, (recipe) => recipeDomain(recipe.name));
48
- if (json) {
49
- console.log(JSON.stringify({ schemaVersion: 1, command, action: "list", adapter, recipes }, null, 2));
50
- return EXIT.ok;
51
- }
52
- const domainStyles = stylesFor(available2.map((recipe) => recipeDomain(recipe.name)));
53
- console.log(out("bold", `runnable recipes (${adapter})`));
54
- for (const line of libraryLines()) console.log(line);
55
- console.log(out("comment", "Inspect: mm-harness run <recipe> --describe"));
56
- if (recipes.length === 0) {
57
- console.log("No runnable recipes match.");
58
- console.log(`Available domains: ${[...domainStyles.keys()].join(", ") || "none"}`);
59
- console.log(`Available sources: ${[...new Set(available2.map((recipe) => recipe.source))].join(", ") || "none"}`);
60
- }
61
- const groups = /* @__PURE__ */ new Map();
62
- for (const recipe of recipes) {
63
- const group = groupKey(recipe, recipeDomain(recipe.name));
64
- groups.set(group, [...groups.get(group) ?? [], recipe]);
65
- }
66
- for (const [group, entries] of groups) {
67
- if (group) {
68
- const label = sort === "library" ? `Library: ${group}` : group;
69
- console.log(`
70
- ${out(sort === "domain" ? domainStyles.get(group) : "bold", label)} ${out("dim", `(${entries.length})`)}`);
71
- }
72
- for (const recipe of entries) {
73
- const style = domainStyles.get(recipeDomain(recipe.name));
74
- const shadowed = recipe.shadows.length > 0 ? ` shadows=${recipe.shadows.join(",")}` : "";
75
- const variant = recipe.variant ? ` variant=${recipe.variant}` : " variant=all";
76
- const summary = catalogDescription(recipe.description);
77
- console.log(` ${out(style, recipe.name)} ${out("dim", `library=${recipe.source}${variant}${shadowed}`)}`);
78
- if (summary) console.log(` ${summary}`);
79
- }
80
- }
81
- return EXIT.ok;
82
- }
83
- const { manifest, actionSources } = await resolveActionManifest(
84
- adapter,
85
- optionString(options, "actionManifest"),
86
- librarySources
87
- );
88
- const { getRecipeActionManifestActionNames } = await importRecipeProtocol();
89
- const protocolNames = new Set(getRecipeActionManifestActionNames(manifest));
90
- const available = describeManifestActions(manifest, actionSources).filter((entry) => protocolNames.has(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
91
- const described = select(available, actionDomain);
92
- const shortByName = shortActionNames(available.map((entry) => entry.name));
93
- const actions = described.map((entry) => ({
94
- name: entry.name,
95
- short: shortByName.get(entry.name) ?? null,
96
- description: entry.description,
97
- fields: entry.fields,
98
- source: entry.source,
99
- sourceTier: entry.sourceTier,
100
- sourceManifest: entry.sourceManifest,
101
- shadows: entry.shadows
102
- }));
103
- if (json) {
104
- console.log(JSON.stringify({ schemaVersion: 1, command, action: "list", adapter, actions }, null, 2));
105
- return EXIT.ok;
106
- }
107
- const contextLines = libraryLines();
108
- if (described.length === 0) {
109
- contextLines.push(`Available domains: ${[...new Set(available.map(actionDomain))].sort().join(", ") || "none"}`);
110
- contextLines.push(`Available sources: ${[...new Set(available.map((entry) => entry.source))].sort().join(", ") || "none"}`);
111
- }
112
- console.log(renderHumanActionCatalog(described, {
113
- title: `invocable actions (${adapter})`,
114
- guidance: "Use: mm-harness call <name> \xB7 short names shown first where unambiguous",
115
- sort,
116
- domainStyles: stylesFor(available.map(actionDomain)),
117
- contextLines,
118
- displayName: (entry) => {
119
- const short = shortByName.get(entry.name);
120
- return short ? `${short} (${entry.name})` : `${entry.name} (full name only \u2014 ambiguous short)`;
121
- }
122
- }));
123
- return EXIT.ok;
124
- }
125
- async function handleDescribeRecipe(recipeArg, options) {
126
- const json = optionFlag(options, "json");
127
- const { adapter } = resolveAdapter(options);
128
- const libraryEntries = optionStrings(options, "library");
129
- const directRecipePath = path.resolve(recipeArg);
130
- const librarySources = await resolveMetaMaskLibrarySources(
131
- libraryEntries,
132
- fs.existsSync(directRecipePath) ? directRecipePath : void 0
133
- );
134
- const result = await describeRunnableRecipe(recipeArg, adapter, librarySources);
135
- const targetEntry = optionString(options, "target");
136
- const replayTarget = targetEntry ? path.resolve(targetEntry) : void 0;
137
- const contextFlags = [
138
- `--adapter ${adapter}`,
139
- ...libraryEntries?.map((entry) => `--library ${shellQuoteArg(entry)}`) ?? [],
140
- ...replayTarget ? [`--target ${shellQuoteArg(replayTarget)}`] : []
141
- ].join(" ");
142
- const discoveryCommand = `mm-harness run --list ${contextFlags}`;
143
- if ("notFound" in result || "unreadable" in result) {
144
- const code = "notFound" in result ? "RECIPE_NOT_FOUND" : "RECIPE_UNPARSEABLE";
145
- const message = "notFound" in result ? result.notFound : result.unreadable;
146
- const userAction = "notFound" in result ? discoveryCommand : `fix ${shellQuoteArg(recipeArg)}, then retry: mm-harness run ${shellQuoteArg(recipeArg)} --describe ${contextFlags}`;
147
- if (json) {
148
- console.log(JSON.stringify({
149
- schemaVersion: 1,
150
- command: "run",
151
- action: "describe",
152
- status: "fail",
153
- error: { code, message, userAction },
154
- exitCode: EXIT.usage
155
- }, null, 2));
156
- } else {
157
- console.error(`\u2717 run: ${message}
158
- Next: ${userAction}`);
159
- }
160
- return EXIT.usage;
161
- }
162
- const recipe = result.recipe;
163
- const parameterArgs = recipe.parameters.filter((parameter) => parameter.required && !Object.hasOwn(parameter, "default")).map((parameter) => `${parameter.name}=${shellQuoteArg(exampleParameterValue(parameter))}`);
164
- const runCommand = [
165
- "mm-harness run",
166
- shellQuoteArg(recipeArg),
167
- ...parameterArgs,
168
- contextFlags
169
- ].filter(Boolean).join(" ");
170
- const nextCommand = `${runCommand} --plan`;
171
- if (json) {
172
- console.log(JSON.stringify({
173
- schemaVersion: 1,
174
- command: "run",
175
- action: "describe",
176
- status: "pass",
177
- recipe,
178
- runCommand,
179
- nextCommand
180
- }, null, 2));
181
- return EXIT.ok;
182
- }
183
- const out = (style, text) => color(style, text, { stream: process.stdout });
184
- console.log(out("bold", `recipe ${recipe.name}`));
185
- if (recipe.title) console.log(` ${out("label", "title:")} ${recipe.title}`);
186
- if (recipe.description) console.log(` ${out("label", "description:")} ${recipe.description}`);
187
- console.log(` ${out("label", "adapter:")} ${recipe.adapter}`);
188
- console.log(` ${out("label", "source:")} ${recipe.source} \xB7 ${recipe.file}`);
189
- console.log(` ${out("label", "path:")} ${out("path", recipe.path)}`);
190
- if (recipe.$schema) console.log(` ${out("label", "schema:")} ${recipe.$schema}`);
191
- console.log(` ${out("label", "variant:")} ${recipe.variant ?? "all adapters"}`);
192
- if (recipe.shadows.length > 0) {
193
- console.log(` ${out("label", "shadows:")} ${recipe.shadows.join(", ")}`);
194
- }
195
- if (recipe.parameters.length === 0) {
196
- console.log(` ${out("label", "parameters:")} none`);
197
- } else {
198
- console.log(` ${out("label", `parameters (${recipe.parameters.length})`)}`);
199
- for (const parameter of recipe.parameters) {
200
- const required = parameter.required ? " required" : "";
201
- const defaultValue = Object.hasOwn(parameter, "default") ? ` default=${compactValue(parameter.default)}` : "";
202
- const choices = parameter.enum ? ` choices=${compactValue(parameter.enum)}` : "";
203
- const description = parameter.description ? ` \u2014 ${parameter.description}` : "";
204
- console.log(
205
- ` ${out("cmd", parameter.name)}${parameter.type ? ` (${parameter.type})` : ""}${required}${defaultValue}${choices}${description}`
206
- );
207
- }
208
- }
209
- renderComposition("actions", recipe.actions, out);
210
- renderComposition("called recipes", recipe.nestedRecipes, out);
211
- if (recipe.unresolvedRecipes.length > 0) {
212
- renderComposition("unresolved recipes", recipe.unresolvedRecipes, out);
213
- }
214
- console.log(` ${out("comment", `Run: ${runCommand}`)}`);
215
- console.log(` ${out("comment", `Next: ${nextCommand}`)}`);
216
- return EXIT.ok;
217
- }
218
- function exampleParameterValue(parameter) {
219
- if (parameter.enum && parameter.enum.length > 0) return stringifyAssignmentValue(parameter.enum[0]);
220
- if (parameter.type === "boolean") return "false";
221
- if (parameter.type === "number" || parameter.type === "integer") return "0";
222
- if (parameter.type === "array") return "[]";
223
- if (parameter.type === "object") return "{}";
224
- return "value";
225
- }
226
- function stringifyAssignmentValue(value) {
227
- return typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
228
- }
229
- function compactValue(value) {
230
- const rendered = JSON.stringify(value);
231
- if (rendered === void 0) return String(value);
232
- return rendered.length <= 180 ? rendered : `${rendered.slice(0, 177)}\u2026`;
233
- }
234
- function renderComposition(label, values, out) {
235
- if (values.length === 0) {
236
- console.log(` ${out("label", `${label}:`)} none`);
237
- return;
238
- }
239
- console.log(` ${out("label", `${label} (${values.length})`)}`);
240
- for (const value of values) console.log(` ${out("cmd", value)}`);
241
- }
242
- export {
243
- handleDescribeRecipe,
244
- handleListExecutables
245
- };