@deeeed/metamask-harness 0.78.0 → 0.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +55 -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 +2 -34
  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/ensure-runner-deps.sh +2 -2
  21. package/adapters/shared/harness-source-fingerprint.mjs +13 -8
  22. package/adapters/shared/open-debug.mjs +2 -2
  23. package/adapters/shared/runner-deps-ready.mjs +1 -1
  24. package/adapters/shared/sync-wallet-fixture.sh +8 -5
  25. package/adapters/terminal/cleanup.mjs +2 -1
  26. package/adapters/terminal/inject.mjs +2 -1
  27. package/adapters/terminal/lib/cdp-client.mjs +1 -1
  28. package/bin/mm-harness +9 -28
  29. package/dist/adapters/checkout-files.js +16 -0
  30. package/dist/adapters/console-capture.js +14 -1
  31. package/dist/adapters/core/actions.js +4 -3
  32. package/dist/adapters/core/overlay.js +1 -1
  33. package/dist/adapters/core/readiness.js +28 -1
  34. package/dist/adapters/core/run.js +3 -3
  35. package/dist/adapters/core/surface.js +22 -5
  36. package/dist/adapters/extension/browser-pid.js +56 -0
  37. package/dist/adapters/extension/console-capture.js +1 -1
  38. package/dist/adapters/extension/doctor-checks.js +1 -1
  39. package/dist/adapters/extension/ensure-ready.js +5 -4
  40. package/dist/adapters/extension/extension-id.js +1 -1
  41. package/dist/adapters/extension/feature-flags.js +1 -1
  42. package/dist/adapters/extension/fixtures.js +1 -2
  43. package/dist/adapters/extension/launch.js +4 -5
  44. package/dist/adapters/extension/launched-browser.js +1 -2
  45. package/dist/adapters/extension/network-observer.js +1 -1
  46. package/dist/adapters/extension/overlay.js +1 -1
  47. package/dist/adapters/extension/product-config.js +1 -1
  48. package/dist/adapters/extension/readiness.js +1 -1
  49. package/dist/adapters/extension/reload.js +1 -1
  50. package/dist/adapters/extension/run.js +24 -10
  51. package/dist/adapters/extension/runtime-decision.js +3 -2
  52. package/dist/adapters/extension/runtime.js +9 -5
  53. package/dist/adapters/extension/surface.js +20 -5
  54. package/dist/adapters/extension/verify.js +2 -2
  55. package/dist/adapters/harness-freshness.js +2 -1
  56. package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
  57. package/dist/adapters/mobile/app-identity.js +10 -0
  58. package/dist/adapters/mobile/doctor-checks.js +1 -1
  59. package/dist/adapters/mobile/fixtures.js +1 -1
  60. package/dist/adapters/mobile/launch.js +27 -28
  61. package/dist/adapters/mobile/lifecycle.js +1 -1
  62. package/dist/adapters/mobile/metro-env.js +9 -59
  63. package/dist/adapters/mobile/performance-observer.js +1 -1
  64. package/dist/adapters/mobile/prepare-target.js +1 -1
  65. package/dist/adapters/mobile/prepare.js +4 -5
  66. package/dist/adapters/mobile/provision.js +1 -1
  67. package/dist/adapters/mobile/readiness.js +1 -1
  68. package/dist/adapters/mobile/recording-target.js +15 -0
  69. package/dist/adapters/mobile/release-artifact-state.js +1 -1
  70. package/dist/adapters/mobile/reload.js +1 -1
  71. package/dist/adapters/mobile/run.js +9 -8
  72. package/dist/adapters/mobile/runtime-decision.js +5 -7
  73. package/dist/adapters/mobile/source-freshness.js +22 -126
  74. package/dist/adapters/mobile/surface.js +31 -6
  75. package/dist/adapters/mobile/ui.js +16 -5
  76. package/dist/adapters/mobile/verify.js +2 -3
  77. package/dist/adapters/performance/cdp-trace.js +1 -1
  78. package/dist/adapters/performance/device-platform.js +1 -1
  79. package/dist/adapters/registry.js +2 -14
  80. package/dist/adapters/slot-ports.js +1 -1
  81. package/dist/adapters/surface.js +1 -0
  82. package/dist/adapters/terminal/console-capture.js +1 -1
  83. package/dist/adapters/terminal/launch.js +1 -1
  84. package/dist/adapters/terminal/run.js +25 -2
  85. package/dist/adapters/terminal/surface.js +12 -4
  86. package/dist/adapters.js +8 -4
  87. package/dist/advisor-policy.js +1 -1
  88. package/dist/checkout-view.js +1 -2
  89. package/dist/cli.js +28 -15
  90. package/dist/command-contract.js +1 -26
  91. package/dist/commands/call.js +10 -24
  92. package/dist/commands/check.js +3 -4
  93. package/dist/commands/checklist.js +1 -1
  94. package/dist/commands/completion-candidates.js +2 -10
  95. package/dist/commands/config.js +2 -2
  96. package/dist/commands/core-readiness.js +12 -7
  97. package/dist/commands/debug.js +3 -3
  98. package/dist/commands/device-target.js +5 -5
  99. package/dist/commands/doctor.js +2 -14
  100. package/dist/commands/domain.js +2 -2
  101. package/dist/commands/ensure-ready.js +1 -7
  102. package/dist/commands/fixtures.js +5 -6
  103. package/dist/commands/help.js +3 -4
  104. package/dist/commands/launch/device.js +10 -17
  105. package/dist/commands/launch/extension.js +3 -9
  106. package/dist/commands/launch/mobile.js +1 -1
  107. package/dist/commands/list-executables.js +1 -3
  108. package/dist/commands/logs.js +3 -3
  109. package/dist/commands/manifest.js +1 -12
  110. package/dist/commands/mobile-device-view.js +4 -2
  111. package/dist/commands/overlay.js +110 -0
  112. package/dist/commands/pin.js +1 -1
  113. package/dist/commands/pr-body.js +2 -2
  114. package/dist/commands/prepare.js +6 -11
  115. package/dist/commands/provision.js +3 -12
  116. package/dist/commands/recipe-advice.js +1 -2
  117. package/dist/commands/recipe-quality.js +2 -3
  118. package/dist/commands/resolve-extension.js +1 -7
  119. package/dist/commands/review.js +4 -4
  120. package/dist/commands/run-engine.js +12 -51
  121. package/dist/commands/run.js +4 -25
  122. package/dist/commands/runtime-decision.js +1 -6
  123. package/dist/commands/runtime-health.js +1 -7
  124. package/dist/commands/runtime-launch.js +1 -13
  125. package/dist/commands/self-test.js +2 -6
  126. package/dist/commands/status-watch.js +1 -2
  127. package/dist/commands/status.js +2 -5
  128. package/dist/commands/task-init.js +2 -3
  129. package/dist/commands/tutorial.js +2 -2
  130. package/dist/commands/update.js +1 -1
  131. package/dist/completions-cache.js +1 -1
  132. package/dist/console-classifier.js +19 -0
  133. package/dist/discovery.js +1 -1
  134. package/dist/doctor.js +3 -4
  135. package/dist/funding-execution-context.js +15 -1
  136. package/dist/host.js +32 -0
  137. package/dist/index.js +1 -6
  138. package/dist/live-tsx.js +49 -0
  139. package/dist/mm-harness-cli.js +2 -4
  140. package/dist/network-observation.js +4 -4
  141. package/dist/paths.js +48 -78
  142. package/dist/performance-observation.js +2 -2
  143. package/dist/progress.js +1 -1
  144. package/dist/recipe-assistance-context.js +2 -4
  145. package/dist/recipe-security.js +2 -59
  146. package/dist/review/knowledge.js +3 -3
  147. package/dist/runner.js +5 -3
  148. package/dist/runtime-context.js +1 -1
  149. package/dist/task-view.js +3 -4
  150. package/docs/CONTRIBUTING.md +3 -3
  151. package/docs/cli-contract.md +2 -2
  152. package/docs/feature-guard.md +90 -0
  153. package/library/README.md +3 -1
  154. package/library/actions/core/perps/_controller.mjs +153 -130
  155. package/library/actions/core/perps/quote_fees.mjs +58 -0
  156. package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
  157. package/library/actions/core/wallet/list_accounts.mjs +14 -57
  158. package/library/actions/extension/feature_flags/state.mjs +1 -1
  159. package/library/actions/extension/platform/cdp.mjs +2 -8
  160. package/library/actions/harness-exports.mjs +1 -6
  161. package/library/actions/mobile/app/network-control.mjs +6 -8
  162. package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
  163. package/library/actions/mobile/platform/bridge.mjs +8 -8
  164. package/library/actions/mobile/platform/native-session-name.mjs +21 -1
  165. package/library/actions/mobile/platform/native-session.mjs +3 -4
  166. package/library/actions/mobile/platform/observe-ui.mjs +12 -6
  167. package/library/actions/mobile/ui/native-navigation.mjs +4 -3
  168. package/library/actions/mobile/wallet/native-ui.mjs +3 -2
  169. package/library/actions/shared/analytics/_adapter.mjs +1 -1
  170. package/library/actions/shared/console-findings.mjs +1 -1
  171. package/library/actions/terminal/platform/runtime.mjs +2 -1
  172. package/library/manifests/core.action-manifest.json +83 -13
  173. package/package.json +8 -8
  174. package/adapters/extension/lib/slot-title.cjs +0 -292
  175. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
  176. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
  177. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
  178. package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
  179. package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
  180. package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
  181. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
  182. package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
  183. package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
  184. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
  185. package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
  186. package/adapters/mobile/coalesce-metro-log.cjs +0 -24
  187. package/adapters/mobile/launch-metro.cjs +0 -77
  188. package/adapters/mobile/metro-config.cjs +0 -98
  189. package/adapters/mobile/metro-log-generation.cjs +0 -106
  190. package/dist/adapters/mobile/frame-metrics.js +0 -118
  191. package/dist/adapters/mobile/video-recorder.js +0 -289
  192. package/dist/artifact-files.js +0 -92
  193. package/dist/checkout-lock.js +0 -149
  194. package/dist/cli-color.js +0 -98
  195. package/dist/command-journal.js +0 -308
  196. package/dist/commands/last.js +0 -60
  197. package/dist/commands/launch/index.js +0 -109
  198. package/dist/commands/parse-args.js +0 -279
  199. package/dist/commands/reload.js +0 -41
  200. package/dist/commands/run-report.js +0 -241
  201. package/dist/commands/shared.js +0 -323
  202. package/dist/commands/stop.js +0 -60
  203. package/dist/devices.js +0 -100
  204. package/dist/execution-provenance.js +0 -332
  205. package/dist/harness.js +0 -451
  206. package/dist/heal-bounds.js +0 -137
  207. package/dist/json-stream.js +0 -60
  208. package/dist/leaf-invoke.js +0 -19
  209. package/dist/library-provenance.js +0 -33
  210. package/dist/live-adapter-contract.js +0 -380
  211. package/dist/recording-target.js +0 -120
  212. package/dist/run-diagnostics.js +0 -364
  213. package/dist/run-recording.js +0 -481
  214. package/dist/runtime-proof.js +0 -90
  215. 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
- };