nfunc-mcp 0.3.0 → 0.5.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 (69) hide show
  1. package/README.md +121 -379
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/mappers/a11yDedupe.js +38 -7
  5. package/dist/mappers/a11yDedupe.js.map +1 -1
  6. package/dist/mappers/defectFormatter.d.ts +9 -1
  7. package/dist/mappers/defectFormatter.js +53 -13
  8. package/dist/mappers/defectFormatter.js.map +1 -1
  9. package/dist/mappers/labFieldComparator.d.ts +62 -0
  10. package/dist/mappers/labFieldComparator.js +134 -0
  11. package/dist/mappers/labFieldComparator.js.map +1 -0
  12. package/dist/mappers/priorityMapper.d.ts +42 -0
  13. package/dist/mappers/priorityMapper.js +58 -0
  14. package/dist/mappers/priorityMapper.js.map +1 -1
  15. package/dist/mappers/psiAggregator.d.ts +130 -0
  16. package/dist/mappers/psiAggregator.js +293 -0
  17. package/dist/mappers/psiAggregator.js.map +1 -0
  18. package/dist/mappers/runComparator.d.ts +85 -0
  19. package/dist/mappers/runComparator.js +165 -0
  20. package/dist/mappers/runComparator.js.map +1 -0
  21. package/dist/mappers/wcagLevels.d.ts +73 -0
  22. package/dist/mappers/wcagLevels.js +320 -0
  23. package/dist/mappers/wcagLevels.js.map +1 -0
  24. package/dist/mappers/webVitalsMapper.d.ts +52 -0
  25. package/dist/mappers/webVitalsMapper.js +131 -0
  26. package/dist/mappers/webVitalsMapper.js.map +1 -0
  27. package/dist/tools/accessibility.d.ts +1 -0
  28. package/dist/tools/accessibility.js +488 -63
  29. package/dist/tools/accessibility.js.map +1 -1
  30. package/dist/tools/lighthouse.js +370 -102
  31. package/dist/tools/lighthouse.js.map +1 -1
  32. package/dist/tools/performanceAudit.d.ts +2 -0
  33. package/dist/tools/performanceAudit.js +446 -0
  34. package/dist/tools/performanceAudit.js.map +1 -0
  35. package/dist/tools/performanceAuditPlan.d.ts +2 -0
  36. package/dist/tools/performanceAuditPlan.js +438 -0
  37. package/dist/tools/performanceAuditPlan.js.map +1 -0
  38. package/dist/utils/batchState.d.ts +75 -0
  39. package/dist/utils/batchState.js +128 -0
  40. package/dist/utils/batchState.js.map +1 -0
  41. package/dist/utils/csvReader.d.ts +20 -0
  42. package/dist/utils/csvReader.js +172 -0
  43. package/dist/utils/csvReader.js.map +1 -0
  44. package/dist/utils/httpClient.d.ts +84 -0
  45. package/dist/utils/httpClient.js +171 -0
  46. package/dist/utils/httpClient.js.map +1 -0
  47. package/dist/utils/outputParsers.js +26 -30
  48. package/dist/utils/outputParsers.js.map +1 -1
  49. package/dist/utils/psiAuth.d.ts +26 -0
  50. package/dist/utils/psiAuth.js +36 -0
  51. package/dist/utils/psiAuth.js.map +1 -0
  52. package/dist/utils/psiParser.d.ts +135 -0
  53. package/dist/utils/psiParser.js +200 -0
  54. package/dist/utils/psiParser.js.map +1 -0
  55. package/dist/utils/publicUrl.d.ts +17 -0
  56. package/dist/utils/publicUrl.js +115 -0
  57. package/dist/utils/publicUrl.js.map +1 -0
  58. package/dist/utils/sitemapReader.d.ts +27 -0
  59. package/dist/utils/sitemapReader.js +272 -0
  60. package/dist/utils/sitemapReader.js.map +1 -0
  61. package/dist/utils/urlClassifier.d.ts +45 -0
  62. package/dist/utils/urlClassifier.js +267 -0
  63. package/dist/utils/urlClassifier.js.map +1 -0
  64. package/dist/utils/urlInput.d.ts +30 -0
  65. package/dist/utils/urlInput.js +130 -0
  66. package/dist/utils/urlInput.js.map +1 -0
  67. package/docs/manual.md +769 -0
  68. package/docs/psi-report-spec.md +174 -0
  69. package/package.json +13 -3
@@ -1,6 +1,14 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
1
3
  import { z } from "zod";
2
4
  import { runShell } from "../utils/shellRunner.js";
3
5
  import { parseLighthouseJSON } from "../utils/outputParsers.js";
6
+ import { extractLabMetrics } from "../utils/psiParser.js";
7
+ import { resolveUrlInputs } from "../utils/urlInput.js";
8
+ import { compareRuns } from "../mappers/runComparator.js";
9
+ import { classifyUrls } from "../utils/urlClassifier.js";
10
+ import { aggregate } from "../mappers/psiAggregator.js";
11
+ import { DEFAULT_MAX_SECONDS_PER_CALL, decodeCursor, encodeCursor, elapsedSeconds, filterCompleted, hasBudget, mergeIndex, readIndex, slugForUrl, } from "../utils/batchState.js";
4
12
  import { shellErrorResponse, parseErrorResponse, } from "../utils/toolResponse.js";
5
13
  import { formatLighthouseFinding } from "../mappers/defectFormatter.js";
6
14
  import { sortFindingsByPriority } from "../mappers/priorityMapper.js";
@@ -19,8 +27,36 @@ import { sortFindingsByPriority } from "../mappers/priorityMapper.js";
19
27
  export function formFactorArgs(ff) {
20
28
  return ff === "desktop" ? ["--preset=desktop"] : ["--form-factor=mobile"];
21
29
  }
30
+ /** A single local Lighthouse run, measured. Used to reserve chunk budget. */
31
+ const TYPICAL_RUN_MS = 40_000;
32
+ const DEFAULT_OUTPUT_DIR = "./lighthouse-reports";
22
33
  const inputShape = {
23
- url: z.string().url(),
34
+ url: z
35
+ .string()
36
+ .min(1)
37
+ .describe("One URL, several comma- or newline-separated URLs, or a path to a CSV " +
38
+ "containing a URL column. More than one URL switches the tool into " +
39
+ "batch mode: results are written to output_dir and a cursor is " +
40
+ "returned to continue with."),
41
+ urls: z
42
+ .array(z.string())
43
+ .optional()
44
+ .describe("Explicit URL list, as an alternative to packing them into `url`."),
45
+ output_dir: z.string().optional(),
46
+ cursor: z.string().optional().describe("Resume token from a previous batch call."),
47
+ max_seconds_per_call: z.number().int().min(30).max(900).optional(),
48
+ skip_completed: z
49
+ .boolean()
50
+ .optional()
51
+ .describe("Batch mode only. Skip URL/form-factor pairs already in the output " +
52
+ "index (default true), so re-running fills gaps instead of redoing work."),
53
+ baseline_dir: z
54
+ .string()
55
+ .optional()
56
+ .describe("Compare this run against a previous run's index: which audits were " +
57
+ "fixed, which still fail, which are newly introduced, and how each " +
58
+ "category score moved. Point it at an earlier output_dir. May be the " +
59
+ "same as output_dir; the baseline is read before anything is written."),
24
60
  categories: z.array(z.string()).optional(),
25
61
  thresholds: z.record(z.string(), z.number()).optional(),
26
62
  form_factor: z
@@ -36,126 +72,358 @@ const inputShape = {
36
72
  "device-specific, since the two profiles render different DOM and " +
37
73
  "genuinely find different accessibility and SEO problems."),
38
74
  };
75
+ /** One Lighthouse invocation. Returns the parsed report and the raw LHR. */
76
+ async function runLighthouseOnce(url, factor, categories) {
77
+ const args = [
78
+ url,
79
+ "--output=json",
80
+ "--quiet",
81
+ "--chrome-flags=--headless",
82
+ ...formFactorArgs(factor),
83
+ ];
84
+ if (categories && categories.length > 0) {
85
+ args.push(`--only-categories=${categories.join(",")}`);
86
+ }
87
+ const result = await runShell("lighthouse", args, { timeoutMs: 180_000 });
88
+ if (!result.stdout)
89
+ return { result };
90
+ try {
91
+ return { run: { factor, parsed: parseLighthouseJSON(result.stdout), raw: result.stdout }, result };
92
+ }
93
+ catch (parseError) {
94
+ return { result, parseError };
95
+ }
96
+ }
97
+ function buildFindings(parsed, thresholds) {
98
+ const out = [];
99
+ for (const audit of parsed.failedAudits) {
100
+ const threshold = thresholds?.[audit.id];
101
+ if (typeof threshold === "number" && audit.score >= threshold)
102
+ continue;
103
+ const finding = formatLighthouseFinding(audit);
104
+ if (finding)
105
+ out.push(finding);
106
+ }
107
+ return out;
108
+ }
109
+ /**
110
+ * Merge findings across form factors on audit id, recording which profiles each
111
+ * affects so "fails on desktop only" is readable straight off the finding.
112
+ */
113
+ function mergeAcrossFactors(runs, thresholds) {
114
+ const merged = new Map();
115
+ for (const { factor, parsed } of runs) {
116
+ for (const finding of buildFindings(parsed, thresholds)) {
117
+ const key = String(finding.evidence["audit_id"]);
118
+ const existing = merged.get(key);
119
+ if (existing)
120
+ existing._ff.push(factor);
121
+ else
122
+ merged.set(key, { ...finding, _ff: [factor] });
123
+ }
124
+ }
125
+ const findings = [...merged.values()].map(({ _ff, ...finding }) => ({
126
+ ...finding,
127
+ evidence: {
128
+ ...finding.evidence,
129
+ affects_form_factors: _ff,
130
+ form_factor_specific: _ff.length === 1,
131
+ },
132
+ }));
133
+ return sortFindingsByPriority(findings);
134
+ }
135
+ const auditIdOf = (f) => String(f.evidence["audit_id"] ?? f.title);
136
+ function toDefects(findings) {
137
+ return findings.map((f) => ({ id: auditIdOf(f), priority: f.priority, title: f.title }));
138
+ }
139
+ function recordsToDefectRefs(records) {
140
+ const refs = [];
141
+ for (const record of records) {
142
+ for (const d of record.defects ?? []) {
143
+ refs.push({ url: record.url, variant: record.variant, id: d.id, priority: d.priority, title: d.title });
144
+ }
145
+ }
146
+ return refs;
147
+ }
148
+ /** Category scores keyed url|variant, for the score-delta half of a comparison. */
149
+ function recordsToScores(records) {
150
+ return new Map(records.map((r) => [`${r.url}|${r.variant}`, r.scores]));
151
+ }
152
+ const text = (payload) => ({
153
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
154
+ });
39
155
  export function registerLighthouseTool(server) {
40
156
  server.registerTool("run_lighthouse", {
41
- description: "Runs Google Lighthouse against a URL and returns a QA-style report " +
42
- "with category scores, TTFB, and prioritised findings (P1/P2/P3). " +
157
+ description: "Runs Google Lighthouse and returns a QA-style report with category " +
158
+ "scores, TTFB, and prioritised findings (P1/P2/P3). " +
159
+ "\n\n" +
160
+ "**Accepts one URL, a comma- or newline-separated list, or a path to a " +
161
+ "CSV with a URL column.** A single URL returns one report immediately. " +
162
+ "Several URLs switch to batch mode: each report is written to " +
163
+ "`output_dir`, a running `_index.json` accumulates, a `cursor` is " +
164
+ "returned to continue with, and the final call adds an `aggregate` " +
165
+ "block with cross-page means, per-template rollups and outliers — " +
166
+ "quote those rather than recomputing them. Re-run with the same URLs " +
167
+ "and no cursor to fill any gaps; completed work is skipped. " +
43
168
  "\n\n" +
44
- "`form_factor` selects the device profile: 'mobile' (default, " +
45
- "throttled slow 4G with 4x CPU slowdown), 'desktop' (unthrottled), or " +
46
- "'both'. **Prefer 'both' when auditing a page properly** — the two " +
47
- "profiles render different DOM and find different defects, not just " +
48
- "different performance numbers. With 'both', `scores` is keyed by " +
49
- "form factor and each finding carries affects_form_factors plus " +
50
- "form_factor_specific, so device-only regressions are obvious. " +
169
+ "`form_factor` selects the device profile: 'desktop' (default, " +
170
+ "unthrottled), 'mobile' (throttled slow 4G, 4x CPU slowdown), or " +
171
+ "'both'. **Prefer 'both' when auditing properly** — the two profiles " +
172
+ "render different DOM and find different defects, not just different " +
173
+ "performance numbers. " +
51
174
  "\n\n" +
52
175
  "Requires the Lighthouse CLI on PATH (`npm install -g lighthouse`) " +
53
- "and a Chrome/Chromium binary available.",
176
+ "and a Chrome/Chromium binary.",
54
177
  inputSchema: inputShape,
55
- }, async ({ url, categories, thresholds, form_factor }) => {
178
+ }, async ({ url, urls, categories, thresholds, form_factor, output_dir, cursor, max_seconds_per_call, skip_completed, baseline_dir, }) => {
56
179
  const requested = form_factor ?? "desktop";
57
180
  const factors = requested === "both" ? ["mobile", "desktop"] : [requested];
58
- const runOne = async (ff) => {
59
- const args = [
60
- url,
61
- "--output=json",
62
- "--quiet",
63
- "--chrome-flags=--headless",
64
- ...formFactorArgs(ff),
65
- ];
66
- if (categories && categories.length > 0) {
67
- args.push(`--only-categories=${categories.join(",")}`);
68
- }
69
- return { ff, result: await runShell("lighthouse", args, { timeoutMs: 180_000 }) };
181
+ let resolved;
182
+ try {
183
+ resolved = resolveUrlInputs(url, urls);
184
+ }
185
+ catch (err) {
186
+ return {
187
+ ...text({ error: "unusable_url_input", message: err.message }),
188
+ isError: true,
189
+ };
190
+ }
191
+ const warnings = [...resolved.warnings];
192
+ // Read the baseline before anything is written, so baseline_dir may be
193
+ // the same directory as output_dir.
194
+ const baselineRecords = baseline_dir
195
+ ? await readIndex(resolve(baseline_dir))
196
+ : null;
197
+ if (baseline_dir && baselineRecords && baselineRecords.length === 0) {
198
+ warnings.push(`No previous results found in ${resolve(baseline_dir)} — nothing to compare against. ` +
199
+ "Run once with output_dir set to create a baseline.");
200
+ }
201
+ const compareWith = async (records) => {
202
+ if (!baselineRecords)
203
+ return undefined;
204
+ const result = compareRuns(resolve(baseline_dir), recordsToDefectRefs(baselineRecords), recordsToDefectRefs(records), {
205
+ baselineScores: recordsToScores(baselineRecords),
206
+ currentScores: recordsToScores(records),
207
+ });
208
+ warnings.push(...result.warnings);
209
+ const { warnings: _dropped, ...rest } = result;
210
+ return rest;
70
211
  };
71
- // Concurrent, so "both" costs little more wall time than a single run.
72
- const runs = await Promise.all(factors.map(runOne));
73
- const parsedByFactor = new Map();
74
- for (const { ff, result } of runs) {
75
- if (!result.stdout) {
76
- if (factors.length === 1) {
77
- return shellErrorResponse("Lighthouse produced no JSON output", result);
212
+ // ---- Single URL: unchanged contract ------------------------------
213
+ if (resolved.urls.length === 1) {
214
+ const target = resolved.urls[0];
215
+ const outcomes = await Promise.all(factors.map((factor) => runLighthouseOnce(target, factor, categories)));
216
+ const good = outcomes.filter((o) => o.run).map((o) => o.run);
217
+ if (good.length === 0) {
218
+ const first = outcomes[0];
219
+ return first.parseError
220
+ ? parseErrorResponse("Failed to parse Lighthouse JSON", first.parseError, first.result)
221
+ : shellErrorResponse("Lighthouse produced no JSON output", first.result);
222
+ }
223
+ // Per-factor records, so a single-URL run can seed or be compared to a
224
+ // baseline exactly like a batch one.
225
+ const singleRecords = good.map(({ factor, parsed, raw }) => {
226
+ const findings = sortFindingsByPriority(buildFindings(parsed, thresholds));
227
+ return {
228
+ url: target,
229
+ variant: factor,
230
+ scores: parsed.categoryScores,
231
+ ttfb_ms: parsed.ttfbMs,
232
+ lab: extractLabMetrics(raw),
233
+ finding_count: findings.length,
234
+ p1_count: findings.filter((f) => f.priority === "P1").length,
235
+ defects: toDefects(findings),
236
+ report_file: "",
237
+ };
238
+ });
239
+ // Only touches disk when asked to: a one-off check stays a one-off.
240
+ let indexFile;
241
+ if (output_dir) {
242
+ const dir = resolve(output_dir);
243
+ await mkdir(dir, { recursive: true });
244
+ for (const [i, { factor, raw }] of good.entries()) {
245
+ const file = `${slugForUrl(target)}_${factor}.json`;
246
+ singleRecords[i].report_file = file;
247
+ await writeFile(join(dir, file), raw, "utf8");
78
248
  }
79
- continue; // one form factor failed; report the other
249
+ indexFile = (await mergeIndex(dir, singleRecords)).indexPath;
80
250
  }
81
- try {
82
- parsedByFactor.set(ff, parseLighthouseJSON(result.stdout));
251
+ const comparison = await compareWith(singleRecords);
252
+ const persisted = indexFile
253
+ ? { output_dir: resolve(output_dir), index_file: indexFile }
254
+ : {};
255
+ if (factors.length === 1) {
256
+ const { parsed } = good[0];
257
+ return text({
258
+ url: target,
259
+ form_factor: factors[0],
260
+ scores: parsed.categoryScores,
261
+ ttfb_ms: parsed.ttfbMs,
262
+ findings: sortFindingsByPriority(buildFindings(parsed, thresholds)),
263
+ ...persisted,
264
+ ...(comparison ? { comparison } : {}),
265
+ ...(warnings.length ? { warnings } : {}),
266
+ });
83
267
  }
84
- catch (err) {
85
- if (factors.length === 1) {
86
- return parseErrorResponse("Failed to parse Lighthouse JSON", err, result);
87
- }
268
+ const scores = {};
269
+ const ttfb = {};
270
+ for (const { factor, parsed } of good) {
271
+ scores[factor] = parsed.categoryScores;
272
+ ttfb[factor] = parsed.ttfbMs;
88
273
  }
274
+ return text({
275
+ url: target,
276
+ form_factor: "both",
277
+ form_factors_run: good.map((g) => g.factor),
278
+ scores,
279
+ ttfb_ms: ttfb,
280
+ findings: mergeAcrossFactors(good, thresholds),
281
+ ...persisted,
282
+ ...(comparison ? { comparison } : {}),
283
+ ...(warnings.length ? { warnings } : {}),
284
+ });
89
285
  }
90
- if (parsedByFactor.size === 0) {
91
- return shellErrorResponse("Lighthouse produced no usable output for any form factor", runs[0].result);
286
+ // ---- Batch -------------------------------------------------------
287
+ const dir = resolve(output_dir ?? DEFAULT_OUTPUT_DIR);
288
+ // Before anything writes into it. mergeIndex creates it too, but that
289
+ // runs after the loop, which is too late for the first report file.
290
+ await mkdir(dir, { recursive: true });
291
+ const budgetMs = (max_seconds_per_call ?? DEFAULT_MAX_SECONDS_PER_CALL) * 1000;
292
+ const startedAt = Date.now();
293
+ const allUnits = resolved.urls.flatMap((u) => factors.map((factor) => ({ url: u, variant: factor })));
294
+ // When comparing, re-measuring is the point — skipping completed work
295
+ // would compare a run against itself.
296
+ const { units, skipped } = await filterCompleted(dir, allUnits, skip_completed ?? !baseline_dir, cursor);
297
+ if (skipped > 0) {
298
+ warnings.push(`Skipped ${skipped} URL/form-factor pair(s) already in ${join(dir, "_index.json")}. ` +
299
+ "Pass skip_completed:false to re-measure them.");
92
300
  }
93
- const buildFindings = (parsed) => {
94
- const out = [];
95
- for (const audit of parsed.failedAudits) {
96
- const threshold = thresholds?.[audit.id];
97
- if (typeof threshold === "number" && audit.score >= threshold)
98
- continue;
99
- const f = formatLighthouseFinding(audit);
100
- if (f)
101
- out.push(f);
102
- }
103
- return out;
104
- };
105
- // Single form factor keeps the original flat report shape.
106
- if (parsedByFactor.size === 1 && factors.length === 1) {
107
- const parsed = parsedByFactor.get(factors[0]);
108
- const findings = buildFindings(parsed);
109
- sortFindingsByPriority(findings);
110
- return {
111
- content: [{ type: "text", text: JSON.stringify({
112
- url,
113
- form_factor: factors[0],
114
- scores: parsed.categoryScores,
115
- ttfb_ms: parsed.ttfbMs,
116
- findings,
117
- }, null, 2) }],
118
- };
301
+ if (units.length === 0) {
302
+ return text({
303
+ complete: true,
304
+ progress: { done: 0, total: 0, failed: 0 },
305
+ message: "Every requested URL/form-factor pair is already in the index.",
306
+ index_file: join(dir, "_index.json"),
307
+ warnings,
308
+ });
119
309
  }
120
- // Both: merge on audit_id and record which form factors each affects, so
121
- // "fails on desktop only" is readable straight off the finding.
122
- const merged = new Map();
123
- const scores = {};
124
- const ttfb = {};
125
- for (const ff of factors) {
126
- const parsed = parsedByFactor.get(ff);
127
- if (!parsed)
310
+ const start = decodeCursor(cursor);
311
+ const records = [];
312
+ const results = [];
313
+ const failures = [];
314
+ let index = start;
315
+ let budgetExhausted = false;
316
+ while (index < units.length) {
317
+ // Sequential, not concurrent. Two Chrome instances on one machine
318
+ // contend for CPU, and this tool's whole job is measuring how long
319
+ // things take — a half-busy machine reports numbers nobody can act on.
320
+ // The single-URL path keeps its concurrency: one page, documented.
321
+ if (!hasBudget(startedAt, budgetMs, TYPICAL_RUN_MS, index - start)) {
322
+ budgetExhausted = true;
323
+ break;
324
+ }
325
+ const unit = units[index];
326
+ const factor = unit.variant;
327
+ const outcome = await runLighthouseOnce(unit.url, factor, categories);
328
+ if (!outcome.run) {
329
+ failures.push({
330
+ url: unit.url,
331
+ form_factor: factor,
332
+ error: outcome.parseError
333
+ ? `unparseable Lighthouse JSON: ${outcome.parseError.message}`
334
+ : outcome.result.stderr.slice(0, 300) || `exit ${outcome.result.exitCode}`,
335
+ });
336
+ index++;
128
337
  continue;
129
- scores[ff] = parsed.categoryScores;
130
- ttfb[ff] = parsed.ttfbMs;
131
- for (const f of buildFindings(parsed)) {
132
- const key = String(f.evidence["audit_id"]);
133
- const existing = merged.get(key);
134
- if (existing)
135
- existing._ff.push(ff);
136
- else
137
- merged.set(key, { ...f, _ff: [ff] });
138
338
  }
339
+ const { parsed, raw } = outcome.run;
340
+ const reportFile = `${slugForUrl(unit.url)}_${factor}.json`;
341
+ // The raw LHR, not the parsed summary: the individual audits are what
342
+ // anyone diagnosing a finding actually needs, and they do not survive
343
+ // parsing. The summary lives in the index.
344
+ await writeFile(join(dir, reportFile), raw, "utf8");
345
+ const findings = sortFindingsByPriority(buildFindings(parsed, thresholds));
346
+ records.push({
347
+ url: unit.url,
348
+ variant: factor,
349
+ scores: parsed.categoryScores,
350
+ ttfb_ms: parsed.ttfbMs,
351
+ lab: extractLabMetrics(raw),
352
+ finding_count: findings.length,
353
+ p1_count: findings.filter((f) => f.priority === "P1").length,
354
+ defects: toDefects(findings),
355
+ report_file: reportFile,
356
+ });
357
+ results.push({
358
+ url: unit.url,
359
+ form_factor: factor,
360
+ scores: parsed.categoryScores,
361
+ ttfb_ms: parsed.ttfbMs,
362
+ findings,
363
+ report_file: reportFile,
364
+ });
365
+ index++;
139
366
  }
140
- const findings = Array.from(merged.values()).map(({ _ff, ...f }) => ({
141
- ...f,
142
- evidence: {
143
- ...f.evidence,
144
- affects_form_factors: _ff,
145
- form_factor_specific: _ff.length === 1,
146
- },
147
- }));
148
- sortFindingsByPriority(findings);
149
- return {
150
- content: [{ type: "text", text: JSON.stringify({
151
- url,
152
- form_factor: "both",
153
- form_factors_run: Array.from(parsedByFactor.keys()),
154
- scores,
155
- ttfb_ms: ttfb,
156
- findings,
157
- }, null, 2) }],
158
- };
367
+ const { indexPath } = await mergeIndex(dir, records);
368
+ const complete = index >= units.length;
369
+ if (budgetExhausted) {
370
+ warnings.push(`Stopped after ${elapsedSeconds(startedAt)}s to stay inside the per-call budget. ` +
371
+ "Call again with the cursor to continue.");
372
+ }
373
+ if (failures.length > 0) {
374
+ warnings.push(`${failures.length} run(s) failed. Call again with the same URLs and no cursor — ` +
375
+ "completed pairs are skipped, so only the gaps are retried.");
376
+ }
377
+ let aggregateBlock;
378
+ let comparison;
379
+ if (complete) {
380
+ const all = await readIndex(dir);
381
+ comparison = await compareWith(all);
382
+ // Group by page template so the rollup says "PDPs average 61" rather
383
+ // than listing twelve product URLs. Same classifier the PSI plan uses.
384
+ const { templates } = classifyUrls(all.map((r) => r.url));
385
+ const templateOf = new Map();
386
+ for (const t of templates) {
387
+ for (const candidate of t.candidates)
388
+ templateOf.set(candidate, { id: t.id, label: t.label });
389
+ }
390
+ const runs = all.map((r) => {
391
+ const t = templateOf.get(r.url);
392
+ return {
393
+ template: t?.id ?? "all",
394
+ label: t?.label ?? "All pages",
395
+ url: r.url,
396
+ strategy: r.variant,
397
+ runs: 1,
398
+ scores: r.scores,
399
+ lab: r.lab,
400
+ field: null,
401
+ comparisons: [],
402
+ findings: [],
403
+ report_file: r.report_file,
404
+ };
405
+ });
406
+ const { lab_vs_field_summary, ...rest } = aggregate(runs);
407
+ // No field data in a local run, so the lab-vs-field block would be an
408
+ // empty shape inviting a wrong conclusion. PSI is where that lives.
409
+ aggregateBlock = { ...rest, field_data: "not available — use run_performance_audit for real-user data" };
410
+ }
411
+ return text({
412
+ complete,
413
+ ...(complete ? {} : { cursor: encodeCursor(index) }),
414
+ progress: { done: index, total: units.length, failed: failures.length },
415
+ input_kind: resolved.kind,
416
+ ...(resolved.source ? { input_source: resolved.source } : {}),
417
+ form_factor: requested,
418
+ output_dir: dir,
419
+ index_file: indexPath,
420
+ results,
421
+ ...(failures.length ? { failures } : {}),
422
+ ...(aggregateBlock ? { aggregate: aggregateBlock } : {}),
423
+ ...(comparison ? { comparison } : {}),
424
+ ...(complete ? {} : { next_step: "Call run_lighthouse again with this cursor and the same input." }),
425
+ warnings,
426
+ });
159
427
  });
160
428
  }
161
429
  //# sourceMappingURL=lighthouse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lighthouse.js","sourceRoot":"","sources":["../../src/tools/lighthouse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lighthouse.js","sourceRoot":"","sources":["../../src/tools/lighthouse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerPerformanceAuditTool(server: McpServer): void;