@deeeed/metamask-harness 0.79.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 (252) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +1 -1
  3. package/adapters/extension/ensure-browser.sh +2 -2
  4. package/adapters/extension/inject.mjs +0 -1
  5. package/adapters/extension/launch-browser.cjs +39 -613
  6. package/adapters/extension/readiness.mjs +2 -5
  7. package/adapters/extension/reattach.sh +2 -2
  8. package/adapters/extension/stamp-runtime-title.cjs +1 -1
  9. package/adapters/manifest.json +16 -64
  10. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
  11. package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
  12. package/adapters/mobile/inject.sh +16 -0
  13. package/adapters/mobile/launch-console-forwarder.cjs +1 -2
  14. package/adapters/mobile/open-device.sh +2 -2
  15. package/adapters/mobile/reload-app.mjs +4 -4
  16. package/adapters/mobile/reset-app-data.sh +1 -1
  17. package/adapters/mobile/start-console-forwarder.sh +3 -2
  18. package/adapters/mobile/start-metro.sh +17 -12
  19. package/adapters/mobile/wait-for-bridge.cjs +2 -2
  20. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  21. package/adapters/shared/open-debug.mjs +2 -2
  22. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  23. package/adapters/terminal/cleanup.mjs +2 -1
  24. package/adapters/terminal/inject.mjs +2 -1
  25. package/adapters/terminal/launch.mjs +17 -13
  26. package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
  27. package/adapters/terminal/lib/readiness.mjs +20 -12
  28. package/adapters/terminal/policy.mjs +35 -0
  29. package/adapters/terminal/verify.mjs +10 -3
  30. package/adapters/terminal/wallet-host.mjs +43 -225
  31. package/bin/mm-harness +9 -28
  32. package/dist/adapters/checkout-files.js +16 -0
  33. package/dist/adapters/console-capture.js +14 -1
  34. package/dist/adapters/core/actions.js +8 -4
  35. package/dist/adapters/core/overlay.js +1 -1
  36. package/dist/adapters/core/readiness.js +28 -1
  37. package/dist/adapters/core/run.js +2 -2
  38. package/dist/adapters/core/surface.js +16 -1
  39. package/dist/adapters/extension/actions.js +6 -2
  40. package/dist/adapters/extension/browser-pid.js +56 -0
  41. package/dist/adapters/extension/console-capture.js +1 -1
  42. package/dist/adapters/extension/doctor-checks.js +1 -1
  43. package/dist/adapters/extension/ensure-ready.js +5 -4
  44. package/dist/adapters/extension/extension-id.js +1 -1
  45. package/dist/adapters/extension/feature-flags.js +1 -1
  46. package/dist/adapters/extension/fixtures.js +1 -2
  47. package/dist/adapters/extension/launch.js +4 -5
  48. package/dist/adapters/extension/launched-browser.js +1 -2
  49. package/dist/adapters/extension/network-observer.js +14 -1
  50. package/dist/adapters/extension/overlay.js +1 -1
  51. package/dist/adapters/extension/product-config.js +1 -1
  52. package/dist/adapters/extension/readiness.js +1 -1
  53. package/dist/adapters/extension/reload.js +1 -1
  54. package/dist/adapters/extension/run.js +135 -14
  55. package/dist/adapters/extension/runtime-decision.js +2 -1
  56. package/dist/adapters/extension/runtime.js +9 -5
  57. package/dist/adapters/extension/surface.js +15 -2
  58. package/dist/adapters/extension/verify.js +2 -2
  59. package/dist/adapters/harness-freshness.js +2 -1
  60. package/dist/adapters/mobile/actions.js +4 -1
  61. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  62. package/dist/adapters/mobile/app-identity.js +10 -0
  63. package/dist/adapters/mobile/doctor-checks.js +1 -1
  64. package/dist/adapters/mobile/fixtures.js +1 -1
  65. package/dist/adapters/mobile/launch.js +27 -28
  66. package/dist/adapters/mobile/lifecycle.js +1 -1
  67. package/dist/adapters/mobile/metro-env.js +9 -59
  68. package/dist/adapters/mobile/network-observer.js +68 -0
  69. package/dist/adapters/mobile/performance-observer.js +2 -4
  70. package/dist/adapters/mobile/prepare-target.js +1 -1
  71. package/dist/adapters/mobile/prepare.js +3 -4
  72. package/dist/adapters/mobile/provision.js +1 -1
  73. package/dist/adapters/mobile/readiness.js +1 -1
  74. package/dist/adapters/mobile/recording-target.js +15 -0
  75. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  76. package/dist/adapters/mobile/reload.js +1 -1
  77. package/dist/adapters/mobile/run.js +39 -15
  78. package/dist/adapters/mobile/runtime-decision.js +2 -4
  79. package/dist/adapters/mobile/source-freshness.js +22 -126
  80. package/dist/adapters/mobile/surface.js +26 -3
  81. package/dist/adapters/mobile/ui.js +16 -5
  82. package/dist/adapters/mobile/verify.js +2 -3
  83. package/dist/adapters/performance/cdp-trace.js +1 -1
  84. package/dist/adapters/performance/device-platform.js +1 -1
  85. package/dist/adapters/perps-order-inputs.js +36 -0
  86. package/dist/adapters/proof-failure.js +11 -0
  87. package/dist/adapters/slot-ports.js +1 -1
  88. package/dist/adapters/surface.js +1 -0
  89. package/dist/adapters/terminal/actions.js +4 -1
  90. package/dist/adapters/terminal/console-capture.js +1 -1
  91. package/dist/adapters/terminal/doctor-checks.js +2 -0
  92. package/dist/adapters/terminal/launch.js +4 -1
  93. package/dist/adapters/terminal/overlay.js +8 -1
  94. package/dist/adapters/terminal/run.js +33 -5
  95. package/dist/adapters/terminal/surface.js +9 -1
  96. package/dist/adapters.js +27 -7
  97. package/dist/advisor-policy.js +1 -1
  98. package/dist/checkout-view.js +1 -2
  99. package/dist/cli.js +59 -21
  100. package/dist/command-contract.js +1 -26
  101. package/dist/commands/check.js +33 -10
  102. package/dist/commands/checklist.js +1 -1
  103. package/dist/commands/completion-candidates.js +6 -14
  104. package/dist/commands/config.js +2 -2
  105. package/dist/commands/core-readiness.js +12 -7
  106. package/dist/commands/debug.js +3 -3
  107. package/dist/commands/device-target.js +5 -5
  108. package/dist/commands/doctor.js +2 -14
  109. package/dist/commands/domain.js +2 -2
  110. package/dist/commands/ensure-ready.js +1 -7
  111. package/dist/commands/fixtures.js +5 -6
  112. package/dist/commands/help.js +3 -4
  113. package/dist/commands/launch/device.js +10 -17
  114. package/dist/commands/launch/extension.js +2 -8
  115. package/dist/commands/launch/mobile.js +1 -1
  116. package/dist/commands/logs.js +3 -3
  117. package/dist/commands/mobile-device-view.js +4 -2
  118. package/dist/commands/overlay.js +110 -0
  119. package/dist/commands/pin.js +1 -1
  120. package/dist/commands/pr-body.js +2 -2
  121. package/dist/commands/prepare.js +4 -9
  122. package/dist/commands/provision.js +3 -12
  123. package/dist/commands/recipe-advice.js +1 -2
  124. package/dist/commands/recipe-quality.js +2 -3
  125. package/dist/commands/resolve-extension.js +1 -7
  126. package/dist/commands/review.js +4 -4
  127. package/dist/commands/runtime-decision.js +1 -6
  128. package/dist/commands/runtime-health.js +1 -7
  129. package/dist/commands/runtime-launch.js +1 -13
  130. package/dist/commands/self-test.js +5 -8
  131. package/dist/commands/status-watch.js +1 -2
  132. package/dist/commands/status.js +2 -5
  133. package/dist/commands/task-init.js +2 -3
  134. package/dist/commands/tutorial.js +2 -2
  135. package/dist/commands/update.js +1 -1
  136. package/dist/completions-cache.js +1 -1
  137. package/dist/console-classifier.js +19 -0
  138. package/dist/doctor.js +2 -3
  139. package/dist/engine.js +57 -0
  140. package/dist/funding-execution-context.js +96 -1
  141. package/dist/host.js +32 -0
  142. package/dist/index.js +1 -6
  143. package/dist/live-tsx.js +49 -0
  144. package/dist/mm-harness-cli.js +4 -6
  145. package/dist/paths.js +34 -64
  146. package/dist/performance-observation.js +3 -5
  147. package/dist/progress.js +1 -1
  148. package/dist/recipe-assistance-context.js +6 -9
  149. package/dist/recipe-security.js +2 -59
  150. package/dist/review/knowledge.js +2 -2
  151. package/dist/runner.js +7 -4
  152. package/dist/runtime-context.js +1 -1
  153. package/dist/task-view.js +2 -3
  154. package/docs/CONTRIBUTING.md +5 -3
  155. package/docs/RECIPES.md +31 -3
  156. package/docs/cli-contract.md +2 -2
  157. package/docs/feature-guard.md +90 -0
  158. package/library/README.md +3 -1
  159. package/library/actions/core/perps/_controller.mjs +153 -134
  160. package/library/actions/core/perps/_rewards.mjs +186 -0
  161. package/library/actions/core/perps/close_positions.mjs +1 -1
  162. package/library/actions/core/perps/place_order.mjs +167 -45
  163. package/library/actions/core/perps/quote_fees.mjs +61 -0
  164. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  165. package/library/actions/extension/feature_flags/state.mjs +1 -1
  166. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  167. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  168. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  169. package/library/actions/extension/perps/perps.mjs +320 -3
  170. package/library/actions/extension/perps/place_order.mjs +3 -1
  171. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  172. package/library/actions/extension/platform/cdp.mjs +73 -11
  173. package/library/actions/harness-exports.mjs +1 -6
  174. package/library/actions/mobile/app/network-control.mjs +6 -8
  175. package/library/actions/mobile/perps/perps.mjs +216 -11
  176. package/library/actions/mobile/perps/place_order.mjs +3 -1
  177. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  178. package/library/actions/mobile/platform/bridge.mjs +8 -8
  179. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  180. package/library/actions/mobile/platform/native-session.mjs +3 -4
  181. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  182. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  183. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  184. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  185. package/library/actions/shared/console-findings.mjs +1 -1
  186. package/library/actions/shared/perps/order-policy.mjs +177 -0
  187. package/library/actions/shared/wallet/fixture-accounts.mjs +84 -0
  188. package/library/actions/terminal/app/launch.mjs +3 -2
  189. package/library/actions/terminal/platform/hud.mjs +2 -1
  190. package/library/actions/terminal/platform/page.mjs +2 -1
  191. package/library/actions/terminal/platform/runtime.mjs +2 -1
  192. package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
  193. package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
  194. package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
  195. package/library/manifests/core.action-manifest.json +87 -13
  196. package/library/manifests/extension.action-manifest.json +4 -0
  197. package/library/manifests/mobile.action-manifest.json +12 -0
  198. package/package.json +8 -9
  199. package/site/assets/help-recipes.json +1 -0
  200. package/adapters/extension/lib/slot-title.cjs +0 -292
  201. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  202. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  203. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  204. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  205. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  206. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  207. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  208. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  209. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  210. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  211. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  212. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  213. package/adapters/mobile/launch-metro.cjs +0 -77
  214. package/adapters/mobile/metro-config.cjs +0 -98
  215. package/adapters/mobile/metro-log-generation.cjs +0 -106
  216. package/adapters/terminal/lib/origin.mjs +0 -30
  217. package/adapters/terminal/lib/page-script.mjs +0 -246
  218. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  219. package/dist/adapters/mobile/frame-metrics.js +0 -118
  220. package/dist/adapters/mobile/video-recorder.js +0 -289
  221. package/dist/artifact-files.js +0 -92
  222. package/dist/checkout-lock.js +0 -149
  223. package/dist/cli-color.js +0 -98
  224. package/dist/command-journal.js +0 -308
  225. package/dist/commands/call.js +0 -685
  226. package/dist/commands/last.js +0 -60
  227. package/dist/commands/launch/index.js +0 -109
  228. package/dist/commands/list-executables.js +0 -247
  229. package/dist/commands/manifest.js +0 -488
  230. package/dist/commands/parse-args.js +0 -279
  231. package/dist/commands/reload.js +0 -41
  232. package/dist/commands/run-engine.js +0 -1340
  233. package/dist/commands/run-report.js +0 -241
  234. package/dist/commands/run.js +0 -751
  235. package/dist/commands/shared.js +0 -323
  236. package/dist/commands/stop.js +0 -60
  237. package/dist/devices.js +0 -100
  238. package/dist/discovery.js +0 -15
  239. package/dist/execution-provenance.js +0 -332
  240. package/dist/harness.js +0 -451
  241. package/dist/heal-bounds.js +0 -137
  242. package/dist/json-stream.js +0 -60
  243. package/dist/leaf-invoke.js +0 -19
  244. package/dist/library-provenance.js +0 -33
  245. package/dist/live-adapter-contract.js +0 -380
  246. package/dist/metamask-action-validation.js +0 -68
  247. package/dist/network-observation.js +0 -290
  248. package/dist/recording-target.js +0 -120
  249. package/dist/run-diagnostics.js +0 -364
  250. package/dist/run-recording.js +0 -481
  251. package/dist/runtime-proof.js +0 -90
  252. package/library/actions/mobile/platform/tool-paths.mjs +0 -123
@@ -1,364 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { createHash } from "node:crypto";
4
- import { getAdapterSurface } from "./adapters/surface.js";
5
- import {
6
- collectorOwnerMarker,
7
- collectorStatus
8
- } from "../library/actions/shared/console-collector.mjs";
9
- import {
10
- allowlistMatch,
11
- classifyConsoleRecord,
12
- consoleRecords,
13
- libraryRootsFromEnv,
14
- loadConsoleAllowlist,
15
- messageSignature
16
- } from "../library/actions/shared/console-findings.mjs";
17
- const MAX_CAPTURE_BYTES = 512 * 1024;
18
- const MAX_FINDINGS = 20;
19
- const MAX_PREVIEW_CHARS = 320;
20
- const RAW_APP_LOG_ARTIFACT = "diagnostics-app-log.txt";
21
- function readRunDiagnosticsDocument(diagnosticsPath) {
22
- if (typeof diagnosticsPath !== "string" || diagnosticsPath.length === 0) return null;
23
- try {
24
- const document = JSON.parse(fs.readFileSync(diagnosticsPath, "utf8"));
25
- return isRunDiagnosticsDocument(document) ? document : null;
26
- } catch {
27
- return null;
28
- }
29
- }
30
- function formatRunDiagnosticsForHuman(diagnostics, adapter) {
31
- if (!diagnostics) {
32
- return [getAdapterSurface(adapter).headless ? `N/A \u2014 ${adapter.charAt(0).toUpperCase()}${adapter.slice(1)} is headless and has no application log source.` : "UNAVAILABLE \u2014 application diagnostics could not be collected for this run."];
33
- }
34
- const status = diagnostics.status === "clean" ? "CLEAN" : diagnostics.status === "review" ? "REVIEW" : "UNAVAILABLE";
35
- const lines = [`${status} \u2014 ${diagnostics.note}`];
36
- for (const finding of diagnostics.findings) {
37
- const count = finding.count > 1 ? ` \xD7${finding.count}` : "";
38
- const source = finding.source ? ` [${finding.source}]` : "";
39
- const allowed = finding.allowlisted ? ` (allowlisted: ${finding.allowlisted.reason})` : "";
40
- lines.push(`${finding.level.toUpperCase()}${count}${source} \u2014 ${finding.preview}${allowed}`);
41
- }
42
- return lines;
43
- }
44
- async function beginRunDiagnostics(adapter, projectRoot) {
45
- const surface = getAdapterSurface(adapter);
46
- const source = surface.appLogSource(projectRoot);
47
- if (!source) return null;
48
- const diagnostics = surface.diagnostics;
49
- const issueBuffer = diagnostics?.issueBuffer?.arm(projectRoot) ? { adapter, projectRoot } : void 0;
50
- let capture;
51
- if (diagnostics?.console) {
52
- const collector = diagnostics.console;
53
- const startError = await collector.start(projectRoot).then(() => void 0, (error) => error instanceof Error ? error.message : String(error));
54
- const { extensionLog, pageLog, pidFile } = collector.files(projectRoot);
55
- const cdpPort = collector.cdpPort();
56
- capture = { adapter, projectRoot, extensionLog, pageLog, pidFile, ...cdpPort ? { cdpPort } : {}, ...startError ? { startError } : {} };
57
- }
58
- const walletLogPath = diagnostics?.walletLog?.(projectRoot) ?? null;
59
- const walletStat = walletLogPath ? safeStat(walletLogPath) : null;
60
- const stat = safeStat(source.path);
61
- return {
62
- source,
63
- offset: stat?.size ?? 0,
64
- ...stat ? { inode: stat.ino } : {},
65
- ...issueBuffer ? { issueBuffer } : {},
66
- ...capture ? { capture } : {},
67
- ...walletLogPath ? { walletLog: { path: walletLogPath, offset: walletStat?.size ?? 0, ...walletStat ? { inode: walletStat.ino } : {} } } : {}
68
- };
69
- }
70
- async function verifyConsoleCapture(capture) {
71
- if (capture.startError) return { verified: false, detail: `the console collector did not start: ${capture.startError}` };
72
- if (!capture.cdpPort) return { verified: false, detail: "no CDP port for the console collector" };
73
- const status = collectorStatus({
74
- pidFile: capture.pidFile,
75
- extensionLog: capture.extensionLog,
76
- marker: collectorOwnerMarker(path.dirname(capture.pidFile)),
77
- cdpPort: capture.cdpPort
78
- });
79
- if (!status.ok) return { verified: false, detail: status.detail };
80
- const control = await getAdapterSurface(capture.adapter).diagnostics.console.verifyControl({
81
- projectRoot: capture.projectRoot,
82
- cdpPort: capture.cdpPort,
83
- extensionLog: capture.extensionLog,
84
- pageLog: capture.pageLog,
85
- pidFile: capture.pidFile
86
- });
87
- return { verified: control.ok, detail: control.ok ? `${status.detail}; ${control.detail}` : control.detail };
88
- }
89
- function isRunDiagnosticsDocument(value) {
90
- if (!isRecord(value) || value.schemaVersion !== 1 || value.scope !== "recipe-run-application") return false;
91
- if (value.status !== "clean" && value.status !== "review" && value.status !== "unavailable") return false;
92
- return typeof value.note === "string" && Array.isArray(value.findings);
93
- }
94
- async function finishRunDiagnostics(baseline, result) {
95
- if (!baseline) return result;
96
- try {
97
- const bufferedIssues = baseline.issueBuffer ? getAdapterSurface(baseline.issueBuffer.adapter).diagnostics?.issueBuffer?.collect(baseline.issueBuffer.projectRoot) : void 0;
98
- const allowlist = loadConsoleAllowlist(recipeLibraryRoots(result.summaryPath));
99
- const capture = baseline.capture ? await verifyConsoleCapture(baseline.capture) : void 0;
100
- const { diagnostics, rawAppLog } = collectRunDiagnosticsCapture(baseline, bufferedIssues, allowlist, capture);
101
- const artifactsDir = path.dirname(result.summaryPath);
102
- const diagnosticsPath = path.join(artifactsDir, "diagnostics.json");
103
- fs.writeFileSync(diagnosticsPath, `${JSON.stringify(diagnostics, null, 2)}
104
- `);
105
- fs.writeFileSync(
106
- path.join(artifactsDir, RAW_APP_LOG_ARTIFACT),
107
- redactDiagnosticText(rawAppLog.toString("utf8"))
108
- );
109
- indexDiagnosticArtifact(result.artifactManifestPath);
110
- indexDiagnosticSummary(result.summaryPath, diagnostics);
111
- return {
112
- ...result,
113
- diagnosticsPath,
114
- sideFindings: {
115
- status: diagnostics.status,
116
- note: diagnostics.note,
117
- counts: diagnostics.counts
118
- }
119
- };
120
- } catch (error) {
121
- const detail = error instanceof Error ? error.message : String(error);
122
- console.warn(`WARN: run diagnostics were unavailable: ${detail}`);
123
- return {
124
- ...result,
125
- sideFindings: {
126
- status: "unavailable",
127
- note: "Run diagnostics could not be collected; the recipe result is unchanged.",
128
- counts: { total: 0, warning: 0, error: 0, exception: 0 }
129
- }
130
- };
131
- }
132
- }
133
- function collectRunDiagnostics(baseline, bufferedIssues, allowlist = null, capture) {
134
- return collectRunDiagnosticsCapture(baseline, bufferedIssues, allowlist, capture).diagnostics;
135
- }
136
- function collectRunDiagnosticsCapture(baseline, bufferedIssues, allowlist = null, capture) {
137
- const stat = safeStat(baseline.source.path);
138
- const source = {
139
- label: baseline.source.label,
140
- path: baseline.source.path,
141
- startOffset: baseline.offset,
142
- endOffset: stat?.size ?? baseline.offset,
143
- inode: void 0,
144
- bytesRead: 0,
145
- truncated: false,
146
- inAppBuffer: bufferedIssues === void 0 ? "n/a" : bufferedIssues === null ? "unavailable" : "collected",
147
- rawArtifact: RAW_APP_LOG_ARTIFACT,
148
- redacted: true
149
- };
150
- let rawAppLog = Buffer.alloc(0);
151
- if (stat) {
152
- const sameFile = baseline.inode === void 0 || baseline.inode === stat.ino;
153
- const startOffset = sameFile && stat.size >= baseline.offset ? baseline.offset : 0;
154
- const available = Math.max(0, stat.size - startOffset);
155
- const bytesToRead = Math.min(available, MAX_CAPTURE_BYTES);
156
- source.startOffset = startOffset;
157
- source.endOffset = stat.size;
158
- source.inode = stat.ino;
159
- source.bytesRead = bytesToRead;
160
- source.truncated = available > MAX_CAPTURE_BYTES;
161
- if (bytesToRead > 0) rawAppLog = readSlice(baseline.source.path, startOffset, bytesToRead);
162
- if (source.truncated) rawAppLog = rawAppLog.subarray(0, rawAppLog.lastIndexOf(10) + 1);
163
- }
164
- const text = rawAppLog.toString("utf8");
165
- const lineFindings = consoleRecords(text).map(classifyRecord).filter((finding) => finding !== null);
166
- applyConsoleAllowlist(lineFindings, allowlist);
167
- const allFindings = dedupeFindings(
168
- [
169
- ...lineFindings,
170
- ...(bufferedIssues ?? []).map(classifyBufferedIssue).filter((finding) => finding !== null),
171
- ...baseline.walletLog ? walletLogFindings(baseline.walletLog) : []
172
- ]
173
- );
174
- const applied = allFindings.filter((finding) => finding.level === "info").length;
175
- allFindings.sort((a, b) => Number(a.level === "info") - Number(b.level === "info"));
176
- const findings = allFindings.slice(0, MAX_FINDINGS).map(stripInternal);
177
- const counts = countFindings(allFindings);
178
- const omittedFindingCount = Math.max(0, allFindings.length - findings.length);
179
- const captureFailed = capture !== void 0 && !capture.verified;
180
- const status = counts.total > 0 ? "review" : !captureFailed && !source.truncated && (stat || bufferedIssues !== void 0 && bufferedIssues !== null) ? "clean" : "unavailable";
181
- const captureNote = (captureFailed ? ` Console capture was not verified: ${capture.detail}.` : "") + (source.truncated ? ` The run's log exceeded ${MAX_CAPTURE_BYTES} bytes; only its start was checked.` : "");
182
- const note = (counts.total > 0 ? `Observed ${counts.total} distinct application warning/error event(s) during the recipe run; showing ${findings.length} preview(s) and omitting ${omittedFindingCount}. Relation to the task is not determined.` : status === "clean" ? "No application warnings or errors were emitted during the recipe run." : "Application diagnostics were unavailable for this run.") + captureNote;
183
- return { diagnostics: {
184
- schemaVersion: 1,
185
- scope: "recipe-run-application",
186
- status,
187
- nonBlocking: true,
188
- note,
189
- source,
190
- counts,
191
- ...allowlist ? { allowlist: { entries: allowlist.entries.length, applied, problems: allowlist.problems } } : {},
192
- ...capture ? { capture } : {},
193
- displayedFindingCount: findings.length,
194
- omittedFindingCount,
195
- findings
196
- }, rawAppLog };
197
- }
198
- function classifyRecord(record) {
199
- const classified = classifyConsoleRecord(record);
200
- if (!classified) return null;
201
- const line = record.line;
202
- const signature = messageSignature(redactDiagnosticText(classified.message));
203
- return {
204
- ...makeFinding(classified.level, line.trim(), `${classified.source}|${signature}`),
205
- source: classified.source,
206
- signature: redactPreview(signature),
207
- firstFrame: classified.firstFrame ? redactPreview(classified.firstFrame) : null,
208
- event: classified.key
209
- };
210
- }
211
- function walletLogFindings(walletLog) {
212
- const stat = safeStat(walletLog.path);
213
- if (!stat) return [];
214
- const sameFile = walletLog.inode === void 0 || walletLog.inode === stat.ino;
215
- const start = sameFile && stat.size >= walletLog.offset ? walletLog.offset : 0;
216
- const findings = [];
217
- for (const line of readSlice(walletLog.path, start, stat.size - start).toString("utf8").split("\n")) {
218
- let entry;
219
- try {
220
- entry = JSON.parse(line);
221
- } catch {
222
- continue;
223
- }
224
- if (!isRecord(entry)) continue;
225
- let text = null;
226
- if (entry.kind === "blocked-mainnet" && entry.probe !== true) text = `blocked-mainnet: ${String(entry.transport ?? "request")} to ${String(entry.url ?? "?")}`;
227
- else if (entry.kind === "refused-mainnet") text = `refused-mainnet: ${String(entry.method ?? "?")} (${String(entry.reason ?? "?")})`;
228
- else if (entry.kind === "unattributed") text = `unattributed wallet ${entry.binding === "request" ? "request" : "log entry"}${typeof entry.method === "string" ? ` (${entry.method})` : ""}`;
229
- if (text) findings.push({ ...makeFinding("error", text, `wallet|${text}`), source: "wallet", signature: text, firstFrame: null });
230
- }
231
- return findings;
232
- }
233
- function classifyBufferedIssue(value) {
234
- if (!isRecord(value) || typeof value.text !== "string") return null;
235
- const rawLevel = String(value.level ?? "").toLowerCase();
236
- const level = rawLevel === "warn" || rawLevel === "warning" ? "warning" : rawLevel === "error" ? "error" : rawLevel === "exception" ? "exception" : null;
237
- return level ? makeFinding(level, value.text) : null;
238
- }
239
- function makeFinding(level, text, groupKey) {
240
- const preview = redactPreview(text.trim());
241
- const identity = groupKey ?? preview.replace(/\b\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?\b/gu, "[TIME]").replace(/\[(?:sw|page:[^\]]+|console:[^\]]+)\]/gu, "[APP]").replace(/^(?:(?:WARN(?:ING)?|ERROR|EXCEPTION|\[TIME\]|\[APP\])\s*)+/u, "");
242
- return {
243
- level,
244
- fingerprint: createHash("sha256").update(`${level}|${identity}`).digest("hex").slice(0, 12),
245
- preview
246
- };
247
- }
248
- function applyConsoleAllowlist(findings, allowlist) {
249
- if (!allowlist) return;
250
- for (const finding of findings) {
251
- if (!finding.event) continue;
252
- const match = allowlistMatch({ source: finding.source, key: finding.event }, allowlist);
253
- if (!match) continue;
254
- finding.allowlisted = { reason: match.reason, entry: match.index, file: match.file, ...match.temporary ? { temporary: true } : {} };
255
- finding.level = "info";
256
- finding.fingerprint = createHash("sha256").update(`${finding.fingerprint}|info|${match.file}#${match.index}`).digest("hex").slice(0, 12);
257
- }
258
- }
259
- function redactPreview(value) {
260
- return redactDiagnosticText(value).slice(0, MAX_PREVIEW_CHARS);
261
- }
262
- function redactDiagnosticText(value) {
263
- return value.replace(/\b(Bearer)\s+\S+/giu, "$1 [REDACTED]").replace(/(["'])(api[-_]?key|password|passphrase|mnemonic|seed(?:Phrase)?|privateKey|secret|token|authorization|vault)\1\s*:\s*(?:"[^"]*"|'[^']*'|[^,\s}\]]+)/giu, '$1$2$1:"[REDACTED]"').replace(/\b(api[-_]?key|password|passphrase|mnemonic|seed(?:Phrase)?|privateKey|secret|token|authorization|vault)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|\S+)/giu, "$1=[REDACTED]").replace(/\b(?:0x)?[a-f0-9]{64,}\b/giu, "[REDACTED_HEX]").replace(/(https?:\/\/[^\s?]+)\?\S+/giu, "$1?[REDACTED_QUERY]");
264
- }
265
- function dedupeFindings(findings) {
266
- const byFingerprint = /* @__PURE__ */ new Map();
267
- for (const finding of findings) {
268
- const existing = byFingerprint.get(finding.fingerprint);
269
- if (existing) existing.count += 1;
270
- else byFingerprint.set(finding.fingerprint, { ...finding, count: 1 });
271
- }
272
- return [...byFingerprint.values()];
273
- }
274
- function countFindings(findings) {
275
- const counts = { total: 0, warning: 0, error: 0, exception: 0, info: 0 };
276
- for (const finding of findings) {
277
- counts[finding.level] += 1;
278
- if (finding.level !== "info") counts.total += 1;
279
- }
280
- return counts;
281
- }
282
- function stripInternal(finding) {
283
- const { event: _event, ...rest } = finding;
284
- return rest;
285
- }
286
- function readSlice(filePath, offset, length) {
287
- const fd = fs.openSync(filePath, "r");
288
- try {
289
- const buffer = Buffer.alloc(length);
290
- const bytesRead = fs.readSync(fd, buffer, 0, length, offset);
291
- return buffer.subarray(0, bytesRead);
292
- } finally {
293
- fs.closeSync(fd);
294
- }
295
- }
296
- function safeStat(filePath) {
297
- try {
298
- return fs.statSync(filePath);
299
- } catch {
300
- return null;
301
- }
302
- }
303
- function indexDiagnosticArtifact(manifestPath) {
304
- const manifest = readJsonRecord(manifestPath);
305
- if (!manifest) return;
306
- const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
307
- manifest.artifacts = [
308
- ...artifacts.filter((artifact) => !isRecord(artifact) || !["diagnostics.json", RAW_APP_LOG_ARTIFACT].includes(String(artifact.path))),
309
- {
310
- path: "diagnostics.json",
311
- type: "json",
312
- label: "Run-scoped application diagnostics",
313
- category: "diagnostic"
314
- },
315
- {
316
- path: RAW_APP_LOG_ARTIFACT,
317
- type: "log",
318
- label: "Redacted run-scoped application log slice",
319
- category: "diagnostic"
320
- }
321
- ];
322
- fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
323
- `);
324
- }
325
- function indexDiagnosticSummary(summaryPath, diagnostics) {
326
- const summary = readJsonRecord(summaryPath);
327
- if (!summary) return;
328
- summary.sideFindings = {
329
- status: diagnostics.status,
330
- nonBlocking: true,
331
- counts: diagnostics.counts,
332
- diagnosticsPath: "diagnostics.json",
333
- appLogPath: RAW_APP_LOG_ARTIFACT,
334
- appLogRedacted: true
335
- };
336
- fs.writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}
337
- `);
338
- }
339
- function recipeLibraryRoots(summaryPath) {
340
- const summary = readJsonRecord(summaryPath);
341
- const libraries = isRecord(summary?.recipeLibraries) ? summary.recipeLibraries : null;
342
- const sources = Array.isArray(libraries?.sources) ? libraries.sources : [];
343
- const roots = sources.map((source) => isRecord(source) && typeof source.root === "string" ? source.root : null).filter((root) => Boolean(root));
344
- return roots.length ? roots : libraryRootsFromEnv();
345
- }
346
- function readJsonRecord(filePath) {
347
- try {
348
- const value = JSON.parse(fs.readFileSync(filePath, "utf8"));
349
- return isRecord(value) ? value : null;
350
- } catch {
351
- return null;
352
- }
353
- }
354
- function isRecord(value) {
355
- return typeof value === "object" && value !== null && !Array.isArray(value);
356
- }
357
- export {
358
- beginRunDiagnostics,
359
- collectRunDiagnostics,
360
- finishRunDiagnostics,
361
- formatRunDiagnosticsForHuman,
362
- readRunDiagnosticsDocument,
363
- verifyConsoleCapture
364
- };