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