@forkpoint/agent-lighthouse 0.4.0 → 2.0.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.
- package/README.md +10 -7
- package/dist/main.js +170 -77
- package/dist/main.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,13 +10,16 @@ npx @forkpoint/agent-lighthouse https://yourstore.com --view
|
|
|
10
10
|
npx @forkpoint/agent-lighthouse https://staging.yourstore.com --min-score 85
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The CLI generates terminal, HTML, JSON, and Markdown reports for
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- AEO/GEO
|
|
13
|
+
The CLI generates terminal, HTML, JSON, and Markdown reports for 215 audits across 8 agent-journey categories:
|
|
14
|
+
|
|
15
|
+
- Access & Crawl Control — robots.txt access for GPTBot, ClaudeBot, PerplexityBot, and other AI crawlers
|
|
16
|
+
- Content Extraction — clean main content, semantic structure, render and response cost
|
|
17
|
+
- Machine Discovery — `llms.txt`, `llms-full.txt`, sitemaps, feeds, and `.well-known` surfaces
|
|
18
|
+
- Structured Data — Schema.org, JSON-LD, product, offer, review, and organization markup
|
|
19
|
+
- Answer Readiness — AEO/GEO answerability, step lists, tables, unique data, and citations
|
|
20
|
+
- Agent Interfaces — WebMCP, OpenAPI, agents.json, and action-surface discovery
|
|
21
|
+
- Agentic Commerce — product offers, availability, checkout, and payment surfaces
|
|
22
|
+
- Agent Operability & Safety — HTTPS, security.txt, tdmrep, stability, and broken agent endpoints
|
|
20
23
|
|
|
21
24
|
## CI
|
|
22
25
|
|
package/dist/main.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
// src/main.ts
|
|
5
|
-
var
|
|
5
|
+
var import_agent_lighthouse_core2 = require("@forkpoint/agent-lighthouse-core");
|
|
6
6
|
|
|
7
7
|
// src/progress-renderer.ts
|
|
8
8
|
var PHASE_LABELS = {
|
|
@@ -111,13 +111,123 @@ function createProgressRenderer(options) {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// src/options.ts
|
|
115
|
+
var import_agent_lighthouse_core = require("@forkpoint/agent-lighthouse-core");
|
|
116
|
+
var DEFAULT_TRACE_FILE = "agent-lighthouse-trace.ndjson";
|
|
117
|
+
function getArgValue(args2, shortFlag, longFlag) {
|
|
118
|
+
for (const arg of args2) {
|
|
119
|
+
if (shortFlag && arg.startsWith(`${shortFlag}=`)) {
|
|
120
|
+
return arg.slice(shortFlag.length + 1);
|
|
121
|
+
}
|
|
122
|
+
if (longFlag && arg.startsWith(`${longFlag}=`)) {
|
|
123
|
+
return arg.slice(longFlag.length + 1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const shortIdx = shortFlag ? args2.indexOf(shortFlag) : -1;
|
|
127
|
+
if (shortIdx !== -1 && args2[shortIdx + 1] && !args2[shortIdx + 1].startsWith("-")) {
|
|
128
|
+
return args2[shortIdx + 1];
|
|
129
|
+
}
|
|
130
|
+
const longIdx = longFlag ? args2.indexOf(longFlag) : -1;
|
|
131
|
+
if (longIdx !== -1 && args2[longIdx + 1] && !args2[longIdx + 1].startsWith("-")) {
|
|
132
|
+
return args2[longIdx + 1];
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
function splitList(value) {
|
|
137
|
+
if (value === void 0) return void 0;
|
|
138
|
+
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
139
|
+
}
|
|
140
|
+
function resolveUrl(positional, fileConfig) {
|
|
141
|
+
return positional || fileConfig.url;
|
|
142
|
+
}
|
|
143
|
+
function isValidUrl(url) {
|
|
144
|
+
try {
|
|
145
|
+
new URL(url);
|
|
146
|
+
return true;
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function parseCliOptions(args2, positionalUrl, fileConfig = {}) {
|
|
152
|
+
const categories = splitList(getArgValue(args2, "", "--categories"));
|
|
153
|
+
const minScoreArg = getArgValue(args2, "", "--min-score");
|
|
154
|
+
return {
|
|
155
|
+
url: resolveUrl(positionalUrl, fileConfig),
|
|
156
|
+
configPath: getArgValue(args2, "-c", "--config"),
|
|
157
|
+
presetName: getArgValue(args2, "-p", "--preset") || fileConfig.preset || "full",
|
|
158
|
+
minScore: minScoreArg ? Number(minScoreArg) : fileConfig.minScore ?? 0,
|
|
159
|
+
outputDir: getArgValue(args2, "-d", "--output-dir") || fileConfig.outputDir || "./reports",
|
|
160
|
+
outputFormats: splitList(getArgValue(args2, "-o", "--output")) ?? fileConfig.output ?? ["terminal", "html", "json"],
|
|
161
|
+
categories,
|
|
162
|
+
unknownCategories: (categories ?? []).filter((c) => !import_agent_lighthouse_core.CATEGORY_IDS.includes(c)),
|
|
163
|
+
includeExperimental: args2.includes("--experimental"),
|
|
164
|
+
isSilent: args2.includes("--silent"),
|
|
165
|
+
progressJson: args2.includes("--progress-json"),
|
|
166
|
+
shouldView: args2.includes("-v") || args2.includes("--view"),
|
|
167
|
+
debugAudit: getArgValue(args2, "", "--debug-audit"),
|
|
168
|
+
// A bare `--trace` with no path is still a request to trace, so it gets
|
|
169
|
+
// the default file rather than being read as "no trace".
|
|
170
|
+
tracePath: args2.includes("--trace") ? getArgValue(args2, "", "--trace") ?? DEFAULT_TRACE_FILE : getArgValue(args2, "", "--trace")
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function resolveCommand(args2) {
|
|
174
|
+
const command = args2[0];
|
|
175
|
+
if (!command || command === "-h" || command === "--help") return { action: "help" };
|
|
176
|
+
if (command === "audit") return { action: "audit", url: args2[1] };
|
|
177
|
+
if (!command.startsWith("-")) return { action: "audit", url: command };
|
|
178
|
+
return { action: "audit" };
|
|
179
|
+
}
|
|
180
|
+
function parseCategoryAssertions(args2, fileConfig = {}) {
|
|
181
|
+
const out = { ...fileConfig.assertCategories ?? {} };
|
|
182
|
+
const record = (pair) => {
|
|
183
|
+
if (!pair) return;
|
|
184
|
+
const [catId, min] = pair.split(":");
|
|
185
|
+
if (catId && min) out[catId] = Number(min);
|
|
186
|
+
};
|
|
187
|
+
for (let i = 0; i < args2.length; i++) {
|
|
188
|
+
const arg = args2[i];
|
|
189
|
+
if (arg.startsWith("--assert-category=")) record(arg.slice("--assert-category=".length));
|
|
190
|
+
else if (arg === "--assert-category") record(args2[i + 1]);
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
function failedAssertion(categories, assertions) {
|
|
195
|
+
for (const [catId, threshold] of Object.entries(assertions)) {
|
|
196
|
+
const matched = categories.find(
|
|
197
|
+
(c) => c.id === catId || c.name.toLowerCase().includes(catId.toLowerCase())
|
|
198
|
+
);
|
|
199
|
+
if (matched && matched.score < threshold) {
|
|
200
|
+
return { name: matched.name, score: matched.score, threshold };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
function selectDebugChecks(checks, debugAudit) {
|
|
206
|
+
if (debugAudit === "fails") {
|
|
207
|
+
return checks.filter((c) => c.status === "fail" || c.status === "warn");
|
|
208
|
+
}
|
|
209
|
+
const needle = debugAudit.toLowerCase();
|
|
210
|
+
return checks.filter((c) => c.id === debugAudit || c.title.toLowerCase().includes(needle));
|
|
211
|
+
}
|
|
212
|
+
function openCommand(platform, filePath) {
|
|
213
|
+
if (platform === "darwin") return `open "${filePath}"`;
|
|
214
|
+
if (platform === "win32") return `start "" "${filePath}"`;
|
|
215
|
+
return `xdg-open "${filePath}"`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/tier-marker.ts
|
|
219
|
+
function tierMarker(tier) {
|
|
220
|
+
if (tier === "informative") return " \x1B[36m(advisory)\x1B[0m";
|
|
221
|
+
if (tier === "experimental") return " \x1B[36m(experimental)\x1B[0m";
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
224
|
+
|
|
114
225
|
// src/main.ts
|
|
115
226
|
var import_agent_lighthouse_report = require("@forkpoint/agent-lighthouse-report");
|
|
116
227
|
var import_node_fs = require("fs");
|
|
117
228
|
var import_node_path = require("path");
|
|
118
229
|
var import_node_child_process = require("child_process");
|
|
119
230
|
var args = process.argv.slice(2);
|
|
120
|
-
var command = args[0];
|
|
121
231
|
function getPackageVersion() {
|
|
122
232
|
try {
|
|
123
233
|
const pkg = JSON.parse(
|
|
@@ -143,8 +253,18 @@ function usage() {
|
|
|
143
253
|
Options:
|
|
144
254
|
-p, --preset <name> Audit preset (ecommerce, saas, content, quick, full) [default: full]
|
|
145
255
|
-c, --config <path> Path to configuration file (e.g. agent-lighthouse.config.json)
|
|
146
|
-
--debug-audit <id|fails> Print deep diagnostic breakdown for specific audit ID
|
|
256
|
+
--debug-audit <id|fails> Print deep diagnostic breakdown for a specific audit ID
|
|
257
|
+
(e.g. structured-data/faqpage-schema) or all fails
|
|
258
|
+
--trace [path] Write one NDJSON record per audit \u2014 outcome, status, score,
|
|
259
|
+
duration and the evidence behind it \u2014 including the audits
|
|
260
|
+
that were skipped or errored. Defaults to
|
|
261
|
+
./agent-lighthouse-trace.ndjson
|
|
147
262
|
--categories <list> Comma-separated list of categories to audit
|
|
263
|
+
(access-crawl-control, content-extraction, machine-discovery,
|
|
264
|
+
structured-data, answer-readiness, agent-interfaces,
|
|
265
|
+
agentic-commerce, operability-safety)
|
|
266
|
+
--experimental Also run experimental-tier audits (excluded by default;
|
|
267
|
+
they are reported but never scored)
|
|
148
268
|
-o, --output <formats> Output formats (comma-separated: terminal, html, json, md) [default: terminal,html,json]
|
|
149
269
|
-d, --output-dir <path> Output directory for generated reports [default: ./reports]
|
|
150
270
|
-v, --view Automatically open the generated HTML report in your browser
|
|
@@ -160,61 +280,40 @@ Options:
|
|
|
160
280
|
Examples:
|
|
161
281
|
npx @forkpoint/agent-lighthouse https://yourstore.com
|
|
162
282
|
npx @forkpoint/agent-lighthouse https://yourstore.com --preset ecommerce
|
|
163
|
-
npx @forkpoint/agent-lighthouse https://yourstore.com --debug-audit
|
|
283
|
+
npx @forkpoint/agent-lighthouse https://yourstore.com --debug-audit structured-data/faqpage-schema
|
|
164
284
|
npx @forkpoint/agent-lighthouse https://staging.yourstore.com --min-score 85
|
|
165
285
|
`);
|
|
166
286
|
process.exit(1);
|
|
167
287
|
}
|
|
168
288
|
function openInBrowser(filePath) {
|
|
169
|
-
|
|
170
|
-
(0, import_node_child_process.exec)(cmd, () => {
|
|
289
|
+
(0, import_node_child_process.exec)(openCommand(process.platform, filePath), () => {
|
|
171
290
|
});
|
|
172
291
|
}
|
|
173
|
-
function getArgValue(shortFlag, longFlag) {
|
|
174
|
-
for (const arg of args) {
|
|
175
|
-
if (shortFlag && arg.startsWith(`${shortFlag}=`)) {
|
|
176
|
-
return arg.slice(shortFlag.length + 1);
|
|
177
|
-
}
|
|
178
|
-
if (longFlag && arg.startsWith(`${longFlag}=`)) {
|
|
179
|
-
return arg.slice(longFlag.length + 1);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const shortIdx = shortFlag ? args.indexOf(shortFlag) : -1;
|
|
183
|
-
if (shortIdx !== -1 && args[shortIdx + 1] && !args[shortIdx + 1].startsWith("-")) {
|
|
184
|
-
return args[shortIdx + 1];
|
|
185
|
-
}
|
|
186
|
-
const longIdx = longFlag ? args.indexOf(longFlag) : -1;
|
|
187
|
-
if (longIdx !== -1 && args[longIdx + 1] && !args[longIdx + 1].startsWith("-")) {
|
|
188
|
-
return args[longIdx + 1];
|
|
189
|
-
}
|
|
190
|
-
return void 0;
|
|
191
|
-
}
|
|
192
292
|
async function audit(targetUrl) {
|
|
193
|
-
const
|
|
194
|
-
const fileConfig = (0,
|
|
195
|
-
const
|
|
293
|
+
const configPath = parseCliOptions(args, targetUrl).configPath;
|
|
294
|
+
const fileConfig = (0, import_agent_lighthouse_core2.loadConfigFile)(configPath);
|
|
295
|
+
const opts = parseCliOptions(args, targetUrl, fileConfig);
|
|
296
|
+
const url = opts.url;
|
|
196
297
|
if (!url) {
|
|
197
298
|
console.error("\x1B[31mError:\x1B[0m No target URL specified.");
|
|
198
299
|
usage();
|
|
199
300
|
}
|
|
200
|
-
|
|
201
|
-
new URL(url);
|
|
202
|
-
} catch {
|
|
301
|
+
if (!isValidUrl(url)) {
|
|
203
302
|
console.error(`\x1B[31mInvalid URL:\x1B[0m ${url}`);
|
|
204
303
|
process.exit(1);
|
|
205
304
|
}
|
|
206
|
-
const isSilent =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
305
|
+
const { isSilent, progressJson, shouldView, debugAudit, minScore, outputDir, tracePath } = opts;
|
|
306
|
+
if (progressJson) import_agent_lighthouse_core2.logger.level = "silent";
|
|
307
|
+
const presetName = opts.presetName;
|
|
308
|
+
const preset = (0, import_agent_lighthouse_core2.getPreset)(presetName);
|
|
309
|
+
const { categories, unknownCategories, includeExperimental, outputFormats } = opts;
|
|
310
|
+
if (unknownCategories.length > 0) {
|
|
311
|
+
console.error(
|
|
312
|
+
`\x1B[31mUnknown category: ${unknownCategories.join(", ")}\x1B[0m
|
|
313
|
+
Valid categories: ${import_agent_lighthouse_core2.CATEGORY_IDS.join(", ")}`
|
|
314
|
+
);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
218
317
|
if (!isSilent) {
|
|
219
318
|
printBanner();
|
|
220
319
|
console.log(
|
|
@@ -225,7 +324,16 @@ async function audit(targetUrl) {
|
|
|
225
324
|
const onEvent = progressJson ? (event) => {
|
|
226
325
|
process.stderr.write(JSON.stringify(event) + "\n");
|
|
227
326
|
} : isSilent ? void 0 : createProgressRenderer({ tty: Boolean(process.stdout.isTTY) });
|
|
228
|
-
const
|
|
327
|
+
const traceFile = tracePath ? (0, import_node_path.resolve)(tracePath) : void 0;
|
|
328
|
+
if (traceFile) (0, import_node_fs.rmSync)(traceFile, { force: true });
|
|
329
|
+
const onAuditTrace = traceFile ? (trace) => (0, import_node_fs.appendFileSync)(traceFile, `${JSON.stringify(trace)}
|
|
330
|
+
`) : void 0;
|
|
331
|
+
const report = await (0, import_agent_lighthouse_core2.runScan)(url, {
|
|
332
|
+
onEvent,
|
|
333
|
+
...categories ? { categories } : {},
|
|
334
|
+
includeExperimental,
|
|
335
|
+
...onAuditTrace ? { onAuditTrace } : {}
|
|
336
|
+
});
|
|
229
337
|
const view = (0, import_agent_lighthouse_report.buildReportView)(report);
|
|
230
338
|
if (outputFormats.includes("terminal") && !isSilent) {
|
|
231
339
|
console.log(
|
|
@@ -268,7 +376,7 @@ async function audit(targetUrl) {
|
|
|
268
376
|
console.log(
|
|
269
377
|
` ${scoreColor}\u2022\x1B[0m ${cat.name.padEnd(36)} : ${scoreColor}${cat.score.toString().padStart(
|
|
270
378
|
3
|
|
271
|
-
)}/100\x1B[0m \x1B[90m(${c.pass}\u2713 ${c.warn}! ${c.fail}\u2717)\x1B[0m`
|
|
379
|
+
)}/100\x1B[0m \x1B[90m(${c.pass}\u2713 ${c.warn}! ${c.fail}\u2717${c.advisory > 0 ? ` ${c.advisory} advisory` : ""})\x1B[0m`
|
|
272
380
|
);
|
|
273
381
|
}
|
|
274
382
|
}
|
|
@@ -278,9 +386,7 @@ async function audit(targetUrl) {
|
|
|
278
386
|
const allChecks = view.groups.flatMap(
|
|
279
387
|
(g) => g.categories.flatMap((c) => [...c.checks, ...c.notApplicable])
|
|
280
388
|
);
|
|
281
|
-
const targetChecks =
|
|
282
|
-
(c) => c.id === debugAudit || c.title.toLowerCase().includes(debugAudit.toLowerCase())
|
|
283
|
-
);
|
|
389
|
+
const targetChecks = selectDebugChecks(allChecks, debugAudit);
|
|
284
390
|
if (targetChecks.length === 0) {
|
|
285
391
|
console.log(
|
|
286
392
|
`\x1B[33m[debugger] No audits found matching: ${debugAudit}\x1B[0m
|
|
@@ -297,7 +403,7 @@ async function audit(targetUrl) {
|
|
|
297
403
|
const statusBadge = check.status === "pass" ? "\x1B[32m[PASS]\x1B[0m" : check.status === "warn" ? "\x1B[33m[WARN]\x1B[0m" : check.status === "fail" ? "\x1B[31m[FAIL]\x1B[0m" : "\x1B[90m[N/A]\x1B[0m";
|
|
298
404
|
console.log(
|
|
299
405
|
`
|
|
300
|
-
${statusBadge} \x1B[1m[${check.id}] ${check.title}\x1B[0m (Score: ${check.score})`
|
|
406
|
+
${statusBadge} \x1B[1m[${check.id}] ${check.title}\x1B[0m (Score: ${check.score})${tierMarker(check.tier)}`
|
|
301
407
|
);
|
|
302
408
|
if (check.pageUrl)
|
|
303
409
|
console.log(` \x1B[90mPage:\x1B[0m ${check.pageUrl}`);
|
|
@@ -347,6 +453,9 @@ ${statusBadge} \x1B[1m[${check.id}] ${check.title}\x1B[0m (Score: ${check.score}
|
|
|
347
453
|
(0, import_node_fs.writeFileSync)(mdPath, mdContent);
|
|
348
454
|
if (!isSilent) console.log(` \x1B[90m\u2022 Markdown Report:\x1B[0m ${mdPath}`);
|
|
349
455
|
}
|
|
456
|
+
if (traceFile && !isSilent) {
|
|
457
|
+
console.log(` \x1B[90m\u2022 Audit trace:\x1B[0m ${traceFile}`);
|
|
458
|
+
}
|
|
350
459
|
if (shouldView && htmlPath) {
|
|
351
460
|
openInBrowser(htmlPath);
|
|
352
461
|
}
|
|
@@ -357,38 +466,22 @@ ${statusBadge} \x1B[1m[${check.id}] ${check.title}\x1B[0m (Score: ${check.score}
|
|
|
357
466
|
);
|
|
358
467
|
process.exit(1);
|
|
359
468
|
}
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const matchedCategory = view.groups.flatMap((g) => g.categories).find(
|
|
369
|
-
(c) => c.id === catId || c.name.toLowerCase().includes(catId.toLowerCase())
|
|
469
|
+
const failed = failedAssertion(
|
|
470
|
+
view.groups.flatMap((g) => g.categories),
|
|
471
|
+
parseCategoryAssertions(args, fileConfig)
|
|
472
|
+
);
|
|
473
|
+
if (failed) {
|
|
474
|
+
console.error(
|
|
475
|
+
`
|
|
476
|
+
\x1B[31m\u2716 Category Assertion Failed:\x1B[0m Category '${failed.name}' scored ${failed.score} (threshold: ${failed.threshold})`
|
|
370
477
|
);
|
|
371
|
-
|
|
372
|
-
console.error(
|
|
373
|
-
`
|
|
374
|
-
\x1B[31m\u2716 Category Assertion Failed:\x1B[0m Category '${matchedCategory.name}' scored ${matchedCategory.score} (threshold: ${threshold})`
|
|
375
|
-
);
|
|
376
|
-
process.exit(1);
|
|
377
|
-
}
|
|
478
|
+
process.exit(1);
|
|
378
479
|
}
|
|
379
480
|
}
|
|
380
481
|
async function main() {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (command === "audit") {
|
|
385
|
-
const url = args[1];
|
|
386
|
-
await audit(url);
|
|
387
|
-
} else if (!command.startsWith("-")) {
|
|
388
|
-
await audit(command);
|
|
389
|
-
} else {
|
|
390
|
-
await audit();
|
|
391
|
-
}
|
|
482
|
+
const resolved = resolveCommand(args);
|
|
483
|
+
if (resolved.action === "help") usage();
|
|
484
|
+
await audit(resolved.url);
|
|
392
485
|
}
|
|
393
486
|
main().catch((err) => {
|
|
394
487
|
console.error("\x1B[31mFatal error:\x1B[0m", err.message ?? err);
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/main.ts","../src/progress-renderer.ts"],"sourcesContent":["import {\n runScan,\n loadConfigFile,\n getPreset,\n logger,\n type PresetName,\n type ScanEvent,\n} from \"@forkpoint/agent-lighthouse-core\";\nimport { createProgressRenderer } from \"./progress-renderer\";\nimport {\n buildReportView,\n generateHtmlReport,\n generateMarkdownSummary,\n} from \"@forkpoint/agent-lighthouse-report\";\nimport { writeFileSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { exec } from \"node:child_process\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction getPackageVersion() {\n try {\n const pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../package.json\"), \"utf8\"),\n ) as { version?: string };\n return pkg.version || \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction printBanner() {\n console.log(`\n\\x1b[1m\\x1b[36m🗼 Agent Lighthouse\\x1b[0m \\x1b[90mv${getPackageVersion()}\\x1b[0m\n\\x1b[90mThe Open-Source Lighthouse for the Agentic Web\\x1b[0m\n`);\n}\n\nfunction usage(): never {\n printBanner();\n console.log(`Usage:\n agent-lighthouse <url> [options]\n agent-lighthouse audit <url> [options]\n\nOptions:\n -p, --preset <name> Audit preset (ecommerce, saas, content, quick, full) [default: full]\n -c, --config <path> Path to configuration file (e.g. agent-lighthouse.config.json)\n --debug-audit <id|fails> Print deep diagnostic breakdown for specific audit ID (e.g. 3.2) or all fails\n --categories <list> Comma-separated list of categories to audit\n -o, --output <formats> Output formats (comma-separated: terminal, html, json, md) [default: terminal,html,json]\n -d, --output-dir <path> Output directory for generated reports [default: ./reports]\n -v, --view Automatically open the generated HTML report in your browser\n --min-score <number> Minimum score (0-100) required to pass CI assertions\n --assert-category <id:min> Per-category assertions (e.g. --assert-category structured-data:90)\n --silent Suppress progress output\n --progress-json Stream scan progress as NDJSON (one ScanEvent per line) to stderr\n and suppress the interactive progress display. Stderr is used so\n NDJSON never interleaves with the terminal report on stdout;\n all scanner logs (including error logs) are silenced to keep the\n stream clean — audit errors still appear in the report itself.\n\nExamples:\n npx @forkpoint/agent-lighthouse https://yourstore.com\n npx @forkpoint/agent-lighthouse https://yourstore.com --preset ecommerce\n npx @forkpoint/agent-lighthouse https://yourstore.com --debug-audit 3.2\n npx @forkpoint/agent-lighthouse https://staging.yourstore.com --min-score 85\n`);\n process.exit(1);\n}\n\nfunction openInBrowser(filePath: string) {\n const cmd =\n process.platform === \"darwin\"\n ? `open \"${filePath}\"`\n : process.platform === \"win32\"\n ? `start \"\" \"${filePath}\"`\n : `xdg-open \"${filePath}\"`;\n exec(cmd, () => {});\n}\n\nfunction getArgValue(shortFlag: string, longFlag: string): string | undefined {\n for (const arg of args) {\n if (shortFlag && arg.startsWith(`${shortFlag}=`)) {\n return arg.slice(shortFlag.length + 1);\n }\n if (longFlag && arg.startsWith(`${longFlag}=`)) {\n return arg.slice(longFlag.length + 1);\n }\n }\n const shortIdx = shortFlag ? args.indexOf(shortFlag) : -1;\n if (\n shortIdx !== -1 &&\n args[shortIdx + 1] &&\n !args[shortIdx + 1].startsWith(\"-\")\n ) {\n return args[shortIdx + 1];\n }\n const longIdx = longFlag ? args.indexOf(longFlag) : -1;\n if (\n longIdx !== -1 &&\n args[longIdx + 1] &&\n !args[longIdx + 1].startsWith(\"-\")\n ) {\n return args[longIdx + 1];\n }\n return undefined;\n}\n\nasync function audit(targetUrl?: string) {\n const customConfigPath = getArgValue(\"-c\", \"--config\");\n const fileConfig = loadConfigFile(customConfigPath);\n\n const url = targetUrl || fileConfig.url;\n if (!url) {\n console.error(\"\\x1b[31mError:\\x1b[0m No target URL specified.\");\n usage();\n }\n\n try {\n new URL(url);\n } catch {\n console.error(`\\x1b[31mInvalid URL:\\x1b[0m ${url}`);\n process.exit(1);\n }\n\n const isSilent = args.includes(\"--silent\");\n const progressJson = args.includes(\"--progress-json\");\n // Keep the NDJSON stream clean: scanner logs also go to stderr.\n if (progressJson) logger.level = \"silent\";\n const shouldView = args.includes(\"-v\") || args.includes(\"--view\");\n const debugAudit = getArgValue(\"\", \"--debug-audit\");\n\n const presetName = (getArgValue(\"-p\", \"--preset\") ||\n fileConfig.preset ||\n \"full\") as PresetName;\n const preset = getPreset(presetName);\n\n const minScoreArg = getArgValue(\"\", \"--min-score\");\n const minScore = minScoreArg\n ? Number(minScoreArg)\n : (fileConfig.minScore ?? 0);\n\n const outputDir =\n getArgValue(\"-d\", \"--output-dir\") || fileConfig.outputDir || \"./reports\";\n\n const outputFormatArg = getArgValue(\"-o\", \"--output\");\n const outputFormats = outputFormatArg\n ? outputFormatArg.split(\",\").map((s) => s.trim())\n : (fileConfig.output ?? [\"terminal\", \"html\", \"json\"]);\n\n if (!isSilent) {\n printBanner();\n console.log(\n `Auditing \\x1b[1m${url}\\x1b[0m using \\x1b[36m${preset.name}\\x1b[0m preset ...\\n`,\n );\n }\n\n // Progress: --progress-json streams raw ScanEvents as NDJSON to stderr (kept\n // off stdout so it can't interleave with the terminal report). Otherwise the\n // interactive renderer animates on a TTY and prints plain phase summaries in\n // CI (non-TTY). --silent suppresses all progress output as before.\n const onEvent = progressJson\n ? (event: ScanEvent) => {\n process.stderr.write(JSON.stringify(event) + \"\\n\");\n }\n : isSilent\n ? undefined\n : createProgressRenderer({ tty: Boolean(process.stdout.isTTY) });\n\n const report = await runScan(url, { onEvent });\n\n const view = buildReportView(report);\n\n // Terminal Output\n if (outputFormats.includes(\"terminal\") && !isSilent) {\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m`,\n );\n console.log(\n `\\x1b[1mOVERALL AGENT READINESS:\\x1b[0m \\x1b[1m${view.overallScore}/100\\x1b[0m (${view.scoreTier.toUpperCase()})`,\n );\n console.log(\n `Target: ${report.url} | Preset: ${preset.name} | Pages: ${view.pagesScanned.length} | Duration: ${(view.durationMs / 1000).toFixed(1)}s`,\n );\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m\\n`,\n );\n\n if (report.wafProtection?.isBlocked) {\n console.log(\n ` \\x1b[41m\\x1b[37m\\x1b[1m 🛡️ BOT PROTECTION WALL DETECTED: ${report.wafProtection.name.toUpperCase()} \\x1b[0m`,\n );\n console.log(\n ` \\x1b[31m⚠️ Diagnosis: ${report.wafProtection.reason}\\x1b[0m`,\n );\n console.log(\n ` \\x1b[90mThis storefront is actively dropping or challenging automated crawler connections.\\x1b[0m`,\n );\n console.log(\n ` \\x1b[90mAI agents (GPTBot, Claude, Perplexity) cannot index or interact with this catalog.\\x1b[0m\\n`,\n );\n }\n\n console.log(`\\x1b[1m📊 CATEGORIES:\\x1b[0m`);\n for (const group of view.groups) {\n console.log(\n `\\n \\x1b[1m${group.label}\\x1b[0m \\x1b[90m—\\x1b[0m ${group.score}/100`,\n );\n for (const cat of group.categories) {\n const c = cat.counts;\n const scoreColor =\n cat.score >= 90\n ? \"\\x1b[32m\"\n : cat.score >= 70\n ? \"\\x1b[34m\"\n : cat.score >= 50\n ? \"\\x1b[33m\"\n : \"\\x1b[31m\";\n console.log(\n ` ${scoreColor}•\\x1b[0m ${cat.name.padEnd(36)} : ${scoreColor}${cat.score\n .toString()\n .padStart(\n 3,\n )}/100\\x1b[0m \\x1b[90m(${c.pass}✓ ${c.warn}! ${c.fail}✗)\\x1b[0m`,\n );\n }\n }\n console.log();\n }\n\n // Audit Debugger Output\n if (debugAudit) {\n const allChecks = view.groups.flatMap((g) =>\n g.categories.flatMap((c) => [...c.checks, ...c.notApplicable]),\n );\n const targetChecks =\n debugAudit === \"fails\"\n ? allChecks.filter((c) => c.status === \"fail\" || c.status === \"warn\")\n : allChecks.filter(\n (c) =>\n c.id === debugAudit ||\n c.title.toLowerCase().includes(debugAudit.toLowerCase()),\n );\n\n if (targetChecks.length === 0) {\n console.log(\n `\\x1b[33m[debugger] No audits found matching: ${debugAudit}\\x1b[0m\\n`,\n );\n } else {\n console.log(\n `\\x1b[1m🔍 AUDIT DEBUGGER DIAGNOSTICS (${targetChecks.length} checks):\\x1b[0m`,\n );\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m`,\n );\n\n for (const check of targetChecks) {\n const statusBadge =\n check.status === \"pass\"\n ? \"\\x1b[32m[PASS]\\x1b[0m\"\n : check.status === \"warn\"\n ? \"\\x1b[33m[WARN]\\x1b[0m\"\n : check.status === \"fail\"\n ? \"\\x1b[31m[FAIL]\\x1b[0m\"\n : \"\\x1b[90m[N/A]\\x1b[0m\";\n\n console.log(\n `\\n${statusBadge} \\x1b[1m[${check.id}] ${check.title}\\x1b[0m (Score: ${check.score})`,\n );\n if (check.pageUrl)\n console.log(` \\x1b[90mPage:\\x1b[0m ${check.pageUrl}`);\n if (check.displayValue)\n console.log(` \\x1b[90mFound:\\x1b[0m ${check.displayValue}`);\n if (check.details?.expected)\n console.log(\n ` \\x1b[90mExpected:\\x1b[0m ${check.details.expected}`,\n );\n if (check.explanation)\n console.log(` \\x1b[90mExplanation:\\x1b[0m ${check.explanation}`);\n if (check.impact)\n console.log(` \\x1b[90mImpact:\\x1b[0m ${check.impact}`);\n if (check.fix)\n console.log(` \\x1b[90mFix:\\x1b[0m ${check.fix}`);\n if (check.details?.code) {\n console.log(` \\x1b[90mCode Example:\\x1b[0m`);\n console.log(\n check.details.code\n .split(\"\\n\")\n .map((line: string) => ` \\x1b[36m${line}\\x1b[0m`)\n .join(\"\\n\"),\n );\n }\n }\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m\\n`,\n );\n }\n }\n\n // Ensure output directory exists\n mkdirSync(resolve(outputDir), { recursive: true });\n\n // JSON Report\n if (outputFormats.includes(\"json\")) {\n const jsonPath = resolve(outputDir, \"agent-lighthouse-report.json\");\n writeFileSync(jsonPath, JSON.stringify(report, null, 2));\n if (!isSilent)\n console.log(` \\x1b[90m• JSON Report:\\x1b[0m ${jsonPath}`);\n }\n\n // HTML Report\n let htmlPath = \"\";\n if (outputFormats.includes(\"html\")) {\n htmlPath = resolve(outputDir, \"agent-lighthouse-report.html\");\n const htmlContent = generateHtmlReport(report);\n writeFileSync(htmlPath, htmlContent);\n if (!isSilent)\n console.log(` \\x1b[90m• HTML Report:\\x1b[0m ${htmlPath}`);\n }\n\n // Markdown Summary\n if (outputFormats.includes(\"md\") || outputFormats.includes(\"markdown\")) {\n const mdPath = resolve(outputDir, \"agent-lighthouse-report.md\");\n const mdContent = generateMarkdownSummary(report);\n writeFileSync(mdPath, mdContent);\n if (!isSilent) console.log(` \\x1b[90m• Markdown Report:\\x1b[0m ${mdPath}`);\n }\n\n if (shouldView && htmlPath) {\n openInBrowser(htmlPath);\n }\n\n // Overall Score Assertion\n if (minScore > 0 && view.overallScore < minScore) {\n console.error(\n `\\n\\x1b[31m✖ CI Assertion Failed:\\x1b[0m Overall score ${view.overallScore} is below minimum threshold ${minScore}`,\n );\n process.exit(1);\n }\n\n // Per-category Assertions\n const categoryAssertions = fileConfig.assertCategories ?? {};\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--assert-category\" && args[i + 1]) {\n const [catId, min] = args[i + 1].split(\":\");\n if (catId && min) categoryAssertions[catId] = Number(min);\n }\n }\n\n for (const [catId, threshold] of Object.entries(categoryAssertions)) {\n const matchedCategory = view.groups\n .flatMap((g) => g.categories)\n .find(\n (c) =>\n c.id === catId || c.name.toLowerCase().includes(catId.toLowerCase()),\n );\n\n if (matchedCategory && matchedCategory.score < threshold) {\n console.error(\n `\\n\\x1b[31m✖ Category Assertion Failed:\\x1b[0m Category '${matchedCategory.name}' scored ${matchedCategory.score} (threshold: ${threshold})`,\n );\n process.exit(1);\n }\n }\n}\n\nasync function main() {\n if (!command || command === \"-h\" || command === \"--help\") {\n usage();\n }\n\n if (command === \"audit\") {\n const url = args[1];\n await audit(url);\n } else if (!command.startsWith(\"-\")) {\n await audit(command);\n } else {\n await audit();\n }\n}\n\nmain().catch((err) => {\n console.error(\"\\x1b[31mFatal error:\\x1b[0m\", err.message ?? err);\n process.exit(1);\n});\n","import type { PhaseId, ScanEvent } from \"@forkpoint/agent-lighthouse-core\";\n\nexport const PHASE_LABELS: Record<PhaseId, string> = {\n \"fetch-root\": \"Root files\",\n \"fetch-pages\": \"Pages\",\n analyze: \"Page analysis\",\n audits: \"Audits\",\n report: \"Report\",\n};\n\nconst SPINNER = [\"|\", \"/\", \"-\", \"\\\\\"];\nconst BAR_WIDTH = 20;\nconst ETA_MIN_FRACTION = 0.05;\nconst ETA_MAX_MS = 5 * 60 * 1000;\n\nexport interface PhaseDoneInfo {\n phase: PhaseId;\n completed: number;\n total: number;\n durationMs: number;\n failures?: number;\n color?: boolean;\n}\n\n/** Permanent one-line summary printed when a phase finishes. */\nexport function formatPhaseDone(info: PhaseDoneInfo): string {\n const color = info.color ?? true;\n const seconds = (info.durationMs / 1000).toFixed(1);\n const check = color ? \"\\x1b[32m✓\\x1b[0m\" : \"✓\";\n let line = `${check} ${PHASE_LABELS[info.phase]} ${info.completed}/${info.total} · ${seconds}s`;\n if ((info.failures ?? 0) > 0) {\n const suffix = `· ${info.failures} errored`;\n line += color ? ` \\x1b[33m${suffix}\\x1b[0m` : ` ${suffix}`;\n }\n return line;\n}\n\n/** Human ETA from overall scan fraction, or null when unreliable/absurd. */\nexport function formatEta(fraction: number, elapsedMs: number): string | null {\n if (fraction <= ETA_MIN_FRACTION || fraction >= 1) return null;\n const etaMs = (elapsedMs * (1 - fraction)) / fraction;\n if (!Number.isFinite(etaMs) || etaMs < 0 || etaMs > ETA_MAX_MS) return null;\n return `~${Math.ceil(etaMs / 1000)}s left`;\n}\n\nexport interface StatusLineInfo {\n spinnerIndex: number;\n label: string;\n completed: number;\n total: number;\n fraction: number;\n elapsedMs: number;\n}\n\n/** The sticky overwriting status line shown while a phase is active. */\nexport function formatStatusLine(info: StatusLineInfo): string {\n const spinner = SPINNER[info.spinnerIndex % SPINNER.length];\n const fraction = Math.min(1, Math.max(0, info.fraction));\n const filled = Math.round(fraction * BAR_WIDTH);\n const bar = \"█\".repeat(filled) + \"░\".repeat(BAR_WIDTH - filled);\n const pct = Math.round(fraction * 100);\n const eta = formatEta(info.fraction, info.elapsedMs);\n const counts = info.total > 0 ? ` ${info.completed}/${info.total}` : \"\";\n return ` \\x1b[36m${spinner}\\x1b[0m ${info.label}${counts} [${bar}] ${pct}%${eta ? ` ${eta}` : \"\"}`;\n}\n\nexport interface ProgressRendererOptions {\n /** TTY: animate a sticky status line. Non-TTY: phase summaries only, no ANSI. */\n tty: boolean;\n write?: (text: string) => void;\n now?: () => number;\n minRenderIntervalMs?: number;\n}\n\n/**\n * Stateful ScanEvent consumer. Returns the `onEvent` handler for runScan.\n * Sticky-line renders are throttled; phase:done lines are always written;\n * scan:done only erases the sticky line (the report output follows).\n */\nexport function createProgressRenderer(\n options: ProgressRendererOptions,\n): (event: ScanEvent) => void {\n const write = options.write ?? ((text: string) => process.stdout.write(text));\n const now = options.now ?? (() => Date.now());\n const minInterval = options.minRenderIntervalMs ?? 30;\n\n let spinnerIndex = 0;\n let lastRender = Number.NEGATIVE_INFINITY;\n let stickyShown = false;\n let label = \"\";\n let completed = 0;\n let total = 0;\n let failures = 0;\n\n const clearSticky = () => {\n if (stickyShown) {\n write(\"\\r\\x1b[K\");\n stickyShown = false;\n }\n };\n\n const renderSticky = (fraction: number, elapsedMs: number) => {\n const t = now();\n if (t - lastRender < minInterval) return;\n lastRender = t;\n spinnerIndex += 1;\n write(\n \"\\r\" +\n formatStatusLine({ spinnerIndex, label, completed, total, fraction, elapsedMs }) +\n \"\\x1b[K\",\n );\n stickyShown = true;\n };\n\n return (event) => {\n switch (event.type) {\n case \"scan:start\":\n break;\n case \"phase:start\":\n label = PHASE_LABELS[event.phase];\n completed = 0;\n total = event.totalUnits;\n failures = 0;\n break;\n case \"unit:done\":\n completed = event.completed;\n total = event.total;\n if (options.tty) renderSticky(event.fraction, event.elapsedMs);\n break;\n case \"unit:fail\":\n // A failed unit still counts as settled work (see ProgressTracker).\n completed += 1;\n failures += 1;\n if (options.tty) renderSticky(event.fraction, event.elapsedMs);\n break;\n case \"phase:done\":\n clearSticky();\n write(\n formatPhaseDone({\n phase: event.phase,\n completed,\n total,\n durationMs: event.durationMs,\n failures,\n color: options.tty,\n }) + \"\\n\",\n );\n failures = 0;\n break;\n case \"scan:done\":\n clearSticky();\n break;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/main.ts","../src/progress-renderer.ts","../src/options.ts","../src/tier-marker.ts"],"sourcesContent":["import {\n runScan,\n loadConfigFile,\n getPreset,\n logger,\n CATEGORY_IDS,\n type ScanEvent,\n type AuditTrace,\n} from \"@forkpoint/agent-lighthouse-core\";\nimport { createProgressRenderer } from \"./progress-renderer\";\nimport {\n parseCliOptions,\n resolveCommand,\n isValidUrl,\n parseCategoryAssertions,\n failedAssertion,\n selectDebugChecks,\n openCommand,\n} from \"./options\";\nimport { tierMarker } from \"./tier-marker\";\nimport {\n buildReportView,\n generateHtmlReport,\n generateMarkdownSummary,\n} from \"@forkpoint/agent-lighthouse-report\";\nimport { writeFileSync, mkdirSync, readFileSync, appendFileSync, rmSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { exec } from \"node:child_process\";\n\nconst args = process.argv.slice(2);\n\nfunction getPackageVersion() {\n try {\n const pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../package.json\"), \"utf8\"),\n ) as { version?: string };\n return pkg.version || \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction printBanner() {\n console.log(`\n\\x1b[1m\\x1b[36m🗼 Agent Lighthouse\\x1b[0m \\x1b[90mv${getPackageVersion()}\\x1b[0m\n\\x1b[90mThe Open-Source Lighthouse for the Agentic Web\\x1b[0m\n`);\n}\n\nfunction usage(): never {\n printBanner();\n console.log(`Usage:\n agent-lighthouse <url> [options]\n agent-lighthouse audit <url> [options]\n\nOptions:\n -p, --preset <name> Audit preset (ecommerce, saas, content, quick, full) [default: full]\n -c, --config <path> Path to configuration file (e.g. agent-lighthouse.config.json)\n --debug-audit <id|fails> Print deep diagnostic breakdown for a specific audit ID\n (e.g. structured-data/faqpage-schema) or all fails\n --trace [path] Write one NDJSON record per audit — outcome, status, score,\n duration and the evidence behind it — including the audits\n that were skipped or errored. Defaults to\n ./agent-lighthouse-trace.ndjson\n --categories <list> Comma-separated list of categories to audit\n (access-crawl-control, content-extraction, machine-discovery,\n structured-data, answer-readiness, agent-interfaces,\n agentic-commerce, operability-safety)\n --experimental Also run experimental-tier audits (excluded by default;\n they are reported but never scored)\n -o, --output <formats> Output formats (comma-separated: terminal, html, json, md) [default: terminal,html,json]\n -d, --output-dir <path> Output directory for generated reports [default: ./reports]\n -v, --view Automatically open the generated HTML report in your browser\n --min-score <number> Minimum score (0-100) required to pass CI assertions\n --assert-category <id:min> Per-category assertions (e.g. --assert-category structured-data:90)\n --silent Suppress progress output\n --progress-json Stream scan progress as NDJSON (one ScanEvent per line) to stderr\n and suppress the interactive progress display. Stderr is used so\n NDJSON never interleaves with the terminal report on stdout;\n all scanner logs (including error logs) are silenced to keep the\n stream clean — audit errors still appear in the report itself.\n\nExamples:\n npx @forkpoint/agent-lighthouse https://yourstore.com\n npx @forkpoint/agent-lighthouse https://yourstore.com --preset ecommerce\n npx @forkpoint/agent-lighthouse https://yourstore.com --debug-audit structured-data/faqpage-schema\n npx @forkpoint/agent-lighthouse https://staging.yourstore.com --min-score 85\n`);\n process.exit(1);\n}\n\nfunction openInBrowser(filePath: string) {\n exec(openCommand(process.platform, filePath), () => {});\n}\n\nasync function audit(targetUrl?: string) {\n const configPath = parseCliOptions(args, targetUrl).configPath;\n const fileConfig = loadConfigFile(configPath);\n const opts = parseCliOptions(args, targetUrl, fileConfig);\n\n const url = opts.url;\n if (!url) {\n console.error(\"\\x1b[31mError:\\x1b[0m No target URL specified.\");\n usage();\n }\n\n if (!isValidUrl(url)) {\n console.error(`\\x1b[31mInvalid URL:\\x1b[0m ${url}`);\n process.exit(1);\n }\n\n const { isSilent, progressJson, shouldView, debugAudit, minScore, outputDir, tracePath } = opts;\n // Keep the NDJSON stream clean: scanner logs also go to stderr.\n if (progressJson) logger.level = \"silent\";\n\n const presetName = opts.presetName;\n const preset = getPreset(presetName);\n\n const { categories, unknownCategories, includeExperimental, outputFormats } = opts;\n if (unknownCategories.length > 0) {\n console.error(\n `\\x1b[31mUnknown category: ${unknownCategories.join(\", \")}\\x1b[0m\\nValid categories: ${CATEGORY_IDS.join(\", \")}`,\n );\n process.exit(1);\n }\n\n if (!isSilent) {\n printBanner();\n console.log(\n `Auditing \\x1b[1m${url}\\x1b[0m using \\x1b[36m${preset.name}\\x1b[0m preset ...\\n`,\n );\n }\n\n // Progress: --progress-json streams raw ScanEvents as NDJSON to stderr (kept\n // off stdout so it can't interleave with the terminal report). Otherwise the\n // interactive renderer animates on a TTY and prints plain phase summaries in\n // CI (non-TTY). --silent suppresses all progress output as before.\n const onEvent = progressJson\n ? (event: ScanEvent) => {\n process.stderr.write(JSON.stringify(event) + \"\\n\");\n }\n : isSilent\n ? undefined\n : createProgressRenderer({ tty: Boolean(process.stdout.isTTY) });\n\n // One NDJSON record per audit, appended as the scan runs so a crash still\n // leaves the trace up to the point it stopped. Truncated first: a trace that\n // silently appended to the previous run's would read as one impossible scan.\n const traceFile = tracePath ? resolve(tracePath) : undefined;\n if (traceFile) rmSync(traceFile, { force: true });\n const onAuditTrace = traceFile\n ? (trace: AuditTrace) => appendFileSync(traceFile, `${JSON.stringify(trace)}\\n`)\n : undefined;\n\n const report = await runScan(url, {\n onEvent,\n ...(categories ? { categories } : {}),\n includeExperimental,\n ...(onAuditTrace ? { onAuditTrace } : {}),\n });\n\n const view = buildReportView(report);\n\n // Terminal Output\n if (outputFormats.includes(\"terminal\") && !isSilent) {\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m`,\n );\n console.log(\n `\\x1b[1mOVERALL AGENT READINESS:\\x1b[0m \\x1b[1m${view.overallScore}/100\\x1b[0m (${view.scoreTier.toUpperCase()})`,\n );\n console.log(\n `Target: ${report.url} | Preset: ${preset.name} | Pages: ${view.pagesScanned.length} | Duration: ${(view.durationMs / 1000).toFixed(1)}s`,\n );\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m\\n`,\n );\n\n if (report.wafProtection?.isBlocked) {\n console.log(\n ` \\x1b[41m\\x1b[37m\\x1b[1m 🛡️ BOT PROTECTION WALL DETECTED: ${report.wafProtection.name.toUpperCase()} \\x1b[0m`,\n );\n console.log(\n ` \\x1b[31m⚠️ Diagnosis: ${report.wafProtection.reason}\\x1b[0m`,\n );\n console.log(\n ` \\x1b[90mThis storefront is actively dropping or challenging automated crawler connections.\\x1b[0m`,\n );\n console.log(\n ` \\x1b[90mAI agents (GPTBot, Claude, Perplexity) cannot index or interact with this catalog.\\x1b[0m\\n`,\n );\n }\n\n console.log(`\\x1b[1m📊 CATEGORIES:\\x1b[0m`);\n for (const group of view.groups) {\n console.log(\n `\\n \\x1b[1m${group.label}\\x1b[0m \\x1b[90m—\\x1b[0m ${group.score}/100`,\n );\n for (const cat of group.categories) {\n const c = cat.counts;\n const scoreColor =\n cat.score >= 90\n ? \"\\x1b[32m\"\n : cat.score >= 70\n ? \"\\x1b[34m\"\n : cat.score >= 50\n ? \"\\x1b[33m\"\n : \"\\x1b[31m\";\n console.log(\n ` ${scoreColor}•\\x1b[0m ${cat.name.padEnd(36)} : ${scoreColor}${cat.score\n .toString()\n .padStart(\n 3,\n )}/100\\x1b[0m \\x1b[90m(${c.pass}✓ ${c.warn}! ${c.fail}✗${c.advisory > 0 ? ` ${c.advisory} advisory` : \"\"})\\x1b[0m`,\n );\n }\n }\n console.log();\n }\n\n // Audit Debugger Output\n if (debugAudit) {\n const allChecks = view.groups.flatMap((g) =>\n g.categories.flatMap((c) => [...c.checks, ...c.notApplicable]),\n );\n const targetChecks = selectDebugChecks(allChecks, debugAudit);\n\n if (targetChecks.length === 0) {\n console.log(\n `\\x1b[33m[debugger] No audits found matching: ${debugAudit}\\x1b[0m\\n`,\n );\n } else {\n console.log(\n `\\x1b[1m🔍 AUDIT DEBUGGER DIAGNOSTICS (${targetChecks.length} checks):\\x1b[0m`,\n );\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m`,\n );\n\n for (const check of targetChecks) {\n const statusBadge =\n check.status === \"pass\"\n ? \"\\x1b[32m[PASS]\\x1b[0m\"\n : check.status === \"warn\"\n ? \"\\x1b[33m[WARN]\\x1b[0m\"\n : check.status === \"fail\"\n ? \"\\x1b[31m[FAIL]\\x1b[0m\"\n : \"\\x1b[90m[N/A]\\x1b[0m\";\n\n console.log(\n `\\n${statusBadge} \\x1b[1m[${check.id}] ${check.title}\\x1b[0m (Score: ${check.score})${tierMarker(check.tier)}`,\n );\n if (check.pageUrl)\n console.log(` \\x1b[90mPage:\\x1b[0m ${check.pageUrl}`);\n if (check.displayValue)\n console.log(` \\x1b[90mFound:\\x1b[0m ${check.displayValue}`);\n if (check.details?.expected)\n console.log(\n ` \\x1b[90mExpected:\\x1b[0m ${check.details.expected}`,\n );\n if (check.explanation)\n console.log(` \\x1b[90mExplanation:\\x1b[0m ${check.explanation}`);\n if (check.impact)\n console.log(` \\x1b[90mImpact:\\x1b[0m ${check.impact}`);\n if (check.fix)\n console.log(` \\x1b[90mFix:\\x1b[0m ${check.fix}`);\n if (check.details?.code) {\n console.log(` \\x1b[90mCode Example:\\x1b[0m`);\n console.log(\n check.details.code\n .split(\"\\n\")\n .map((line: string) => ` \\x1b[36m${line}\\x1b[0m`)\n .join(\"\\n\"),\n );\n }\n }\n console.log(\n `\\x1b[1m────────────────────────────────────────────────────────────────────────\\x1b[0m\\n`,\n );\n }\n }\n\n // Ensure output directory exists\n mkdirSync(resolve(outputDir), { recursive: true });\n\n // JSON Report\n if (outputFormats.includes(\"json\")) {\n const jsonPath = resolve(outputDir, \"agent-lighthouse-report.json\");\n writeFileSync(jsonPath, JSON.stringify(report, null, 2));\n if (!isSilent)\n console.log(` \\x1b[90m• JSON Report:\\x1b[0m ${jsonPath}`);\n }\n\n // HTML Report\n let htmlPath = \"\";\n if (outputFormats.includes(\"html\")) {\n htmlPath = resolve(outputDir, \"agent-lighthouse-report.html\");\n const htmlContent = generateHtmlReport(report);\n writeFileSync(htmlPath, htmlContent);\n if (!isSilent)\n console.log(` \\x1b[90m• HTML Report:\\x1b[0m ${htmlPath}`);\n }\n\n // Markdown Summary\n if (outputFormats.includes(\"md\") || outputFormats.includes(\"markdown\")) {\n const mdPath = resolve(outputDir, \"agent-lighthouse-report.md\");\n const mdContent = generateMarkdownSummary(report);\n writeFileSync(mdPath, mdContent);\n if (!isSilent) console.log(` \\x1b[90m• Markdown Report:\\x1b[0m ${mdPath}`);\n }\n\n if (traceFile && !isSilent) {\n console.log(` \\x1b[90m• Audit trace:\\x1b[0m ${traceFile}`);\n }\n\n if (shouldView && htmlPath) {\n openInBrowser(htmlPath);\n }\n\n // Overall Score Assertion\n if (minScore > 0 && view.overallScore < minScore) {\n console.error(\n `\\n\\x1b[31m✖ CI Assertion Failed:\\x1b[0m Overall score ${view.overallScore} is below minimum threshold ${minScore}`,\n );\n process.exit(1);\n }\n\n // Per-category Assertions\n const failed = failedAssertion(\n view.groups.flatMap((g) => g.categories),\n parseCategoryAssertions(args, fileConfig),\n );\n if (failed) {\n console.error(\n `\\n\\x1b[31m✖ Category Assertion Failed:\\x1b[0m Category '${failed.name}' scored ${failed.score} (threshold: ${failed.threshold})`,\n );\n process.exit(1);\n }\n}\n\nasync function main() {\n const resolved = resolveCommand(args);\n if (resolved.action === \"help\") usage();\n await audit(resolved.url);\n}\n\nmain().catch((err) => {\n console.error(\"\\x1b[31mFatal error:\\x1b[0m\", err.message ?? err);\n process.exit(1);\n});\n","import type { PhaseId, ScanEvent } from \"@forkpoint/agent-lighthouse-core\";\n\nexport const PHASE_LABELS: Record<PhaseId, string> = {\n \"fetch-root\": \"Root files\",\n \"fetch-pages\": \"Pages\",\n analyze: \"Page analysis\",\n audits: \"Audits\",\n report: \"Report\",\n};\n\nconst SPINNER = [\"|\", \"/\", \"-\", \"\\\\\"];\nconst BAR_WIDTH = 20;\nconst ETA_MIN_FRACTION = 0.05;\nconst ETA_MAX_MS = 5 * 60 * 1000;\n\nexport interface PhaseDoneInfo {\n phase: PhaseId;\n completed: number;\n total: number;\n durationMs: number;\n failures?: number;\n color?: boolean;\n}\n\n/** Permanent one-line summary printed when a phase finishes. */\nexport function formatPhaseDone(info: PhaseDoneInfo): string {\n const color = info.color ?? true;\n const seconds = (info.durationMs / 1000).toFixed(1);\n const check = color ? \"\\x1b[32m✓\\x1b[0m\" : \"✓\";\n let line = `${check} ${PHASE_LABELS[info.phase]} ${info.completed}/${info.total} · ${seconds}s`;\n if ((info.failures ?? 0) > 0) {\n const suffix = `· ${info.failures} errored`;\n line += color ? ` \\x1b[33m${suffix}\\x1b[0m` : ` ${suffix}`;\n }\n return line;\n}\n\n/** Human ETA from overall scan fraction, or null when unreliable/absurd. */\nexport function formatEta(fraction: number, elapsedMs: number): string | null {\n if (fraction <= ETA_MIN_FRACTION || fraction >= 1) return null;\n const etaMs = (elapsedMs * (1 - fraction)) / fraction;\n if (!Number.isFinite(etaMs) || etaMs < 0 || etaMs > ETA_MAX_MS) return null;\n return `~${Math.ceil(etaMs / 1000)}s left`;\n}\n\nexport interface StatusLineInfo {\n spinnerIndex: number;\n label: string;\n completed: number;\n total: number;\n fraction: number;\n elapsedMs: number;\n}\n\n/** The sticky overwriting status line shown while a phase is active. */\nexport function formatStatusLine(info: StatusLineInfo): string {\n const spinner = SPINNER[info.spinnerIndex % SPINNER.length];\n const fraction = Math.min(1, Math.max(0, info.fraction));\n const filled = Math.round(fraction * BAR_WIDTH);\n const bar = \"█\".repeat(filled) + \"░\".repeat(BAR_WIDTH - filled);\n const pct = Math.round(fraction * 100);\n const eta = formatEta(info.fraction, info.elapsedMs);\n const counts = info.total > 0 ? ` ${info.completed}/${info.total}` : \"\";\n return ` \\x1b[36m${spinner}\\x1b[0m ${info.label}${counts} [${bar}] ${pct}%${eta ? ` ${eta}` : \"\"}`;\n}\n\nexport interface ProgressRendererOptions {\n /** TTY: animate a sticky status line. Non-TTY: phase summaries only, no ANSI. */\n tty: boolean;\n write?: (text: string) => void;\n now?: () => number;\n minRenderIntervalMs?: number;\n}\n\n/**\n * Stateful ScanEvent consumer. Returns the `onEvent` handler for runScan.\n * Sticky-line renders are throttled; phase:done lines are always written;\n * scan:done only erases the sticky line (the report output follows).\n */\nexport function createProgressRenderer(\n options: ProgressRendererOptions,\n): (event: ScanEvent) => void {\n const write = options.write ?? ((text: string) => process.stdout.write(text));\n const now = options.now ?? (() => Date.now());\n const minInterval = options.minRenderIntervalMs ?? 30;\n\n let spinnerIndex = 0;\n let lastRender = Number.NEGATIVE_INFINITY;\n let stickyShown = false;\n let label = \"\";\n let completed = 0;\n let total = 0;\n let failures = 0;\n\n const clearSticky = () => {\n if (stickyShown) {\n write(\"\\r\\x1b[K\");\n stickyShown = false;\n }\n };\n\n const renderSticky = (fraction: number, elapsedMs: number) => {\n const t = now();\n if (t - lastRender < minInterval) return;\n lastRender = t;\n spinnerIndex += 1;\n write(\n \"\\r\" +\n formatStatusLine({ spinnerIndex, label, completed, total, fraction, elapsedMs }) +\n \"\\x1b[K\",\n );\n stickyShown = true;\n };\n\n return (event) => {\n switch (event.type) {\n case \"scan:start\":\n break;\n case \"phase:start\":\n label = PHASE_LABELS[event.phase];\n completed = 0;\n total = event.totalUnits;\n failures = 0;\n break;\n case \"unit:done\":\n completed = event.completed;\n total = event.total;\n if (options.tty) renderSticky(event.fraction, event.elapsedMs);\n break;\n case \"unit:fail\":\n // A failed unit still counts as settled work (see ProgressTracker).\n completed += 1;\n failures += 1;\n if (options.tty) renderSticky(event.fraction, event.elapsedMs);\n break;\n case \"phase:done\":\n clearSticky();\n write(\n formatPhaseDone({\n phase: event.phase,\n completed,\n total,\n durationMs: event.durationMs,\n failures,\n color: options.tty,\n }) + \"\\n\",\n );\n failures = 0;\n break;\n case \"scan:done\":\n clearSticky();\n break;\n }\n };\n}\n","import { CATEGORY_IDS, type PresetName } from \"@forkpoint/agent-lighthouse-core\";\n\n/**\n * Argument parsing, lifted out of `main.ts`.\n *\n * `main.ts` reads `process.argv` at module scope and calls `main()` on import,\n * so nothing in it could be exercised by a test. Everything here is a pure\n * function of the argv array and the config file, which is where every flag\n * bug this CLI has shipped actually lived — `--categories` was in the help text\n * for a whole major version without being parsed at all.\n *\n * Effects stay in `main.ts`: this module never writes to a stream and never\n * calls `process.exit`.\n */\n\n/** Where `--trace` writes when it is given no path of its own. */\nexport const DEFAULT_TRACE_FILE = \"agent-lighthouse-trace.ndjson\";\n\n/** The subset of a config file that the flags override. */\nexport interface FileConfig {\n url?: string;\n preset?: string;\n minScore?: number;\n outputDir?: string;\n output?: string[];\n}\n\nexport interface CliOptions {\n url: string | undefined;\n configPath: string | undefined;\n presetName: PresetName;\n minScore: number;\n outputDir: string;\n outputFormats: string[];\n categories: string[] | undefined;\n /** Names passed to `--categories` that no category answers to. */\n unknownCategories: string[];\n includeExperimental: boolean;\n isSilent: boolean;\n progressJson: boolean;\n shouldView: boolean;\n debugAudit: string | undefined;\n /** Where to write the per-audit NDJSON trace, if `--trace` was given. */\n tracePath: string | undefined;\n}\n\n/**\n * Read one flag's value, in either `--flag=value` or `--flag value` form.\n *\n * A following token that starts with `-` is treated as the next flag rather\n * than as this one's value, so `--preset --silent` reports no preset instead of\n * silently scanning with a preset named \"--silent\".\n */\nexport function getArgValue(\n args: string[],\n shortFlag: string,\n longFlag: string,\n): string | undefined {\n for (const arg of args) {\n if (shortFlag && arg.startsWith(`${shortFlag}=`)) {\n return arg.slice(shortFlag.length + 1);\n }\n if (longFlag && arg.startsWith(`${longFlag}=`)) {\n return arg.slice(longFlag.length + 1);\n }\n }\n const shortIdx = shortFlag ? args.indexOf(shortFlag) : -1;\n if (shortIdx !== -1 && args[shortIdx + 1] && !args[shortIdx + 1]!.startsWith(\"-\")) {\n return args[shortIdx + 1];\n }\n const longIdx = longFlag ? args.indexOf(longFlag) : -1;\n if (longIdx !== -1 && args[longIdx + 1] && !args[longIdx + 1]!.startsWith(\"-\")) {\n return args[longIdx + 1];\n }\n return undefined;\n}\n\n/** Split a comma-separated flag value, dropping empty entries. */\nexport function splitList(value: string | undefined): string[] | undefined {\n if (value === undefined) return undefined;\n return value\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\n/** The target URL: the positional argument wins over the config file. */\nexport function resolveUrl(positional: string | undefined, fileConfig: FileConfig): string | undefined {\n return positional || fileConfig.url;\n}\n\n/** Whether a string parses as an absolute URL. */\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve every option from argv and the config file.\n *\n * Precedence is flag, then config file, then default — the order the help text\n * documents.\n */\nexport function parseCliOptions(\n args: string[],\n positionalUrl: string | undefined,\n fileConfig: FileConfig = {},\n): CliOptions {\n const categories = splitList(getArgValue(args, \"\", \"--categories\"));\n const minScoreArg = getArgValue(args, \"\", \"--min-score\");\n\n return {\n url: resolveUrl(positionalUrl, fileConfig),\n configPath: getArgValue(args, \"-c\", \"--config\"),\n presetName: (getArgValue(args, \"-p\", \"--preset\") || fileConfig.preset || \"full\") as PresetName,\n minScore: minScoreArg ? Number(minScoreArg) : (fileConfig.minScore ?? 0),\n outputDir: getArgValue(args, \"-d\", \"--output-dir\") || fileConfig.outputDir || \"./reports\",\n outputFormats:\n splitList(getArgValue(args, \"-o\", \"--output\")) ??\n fileConfig.output ?? [\"terminal\", \"html\", \"json\"],\n categories,\n unknownCategories: (categories ?? []).filter((c) => !CATEGORY_IDS.includes(c)),\n includeExperimental: args.includes(\"--experimental\"),\n isSilent: args.includes(\"--silent\"),\n progressJson: args.includes(\"--progress-json\"),\n shouldView: args.includes(\"-v\") || args.includes(\"--view\"),\n debugAudit: getArgValue(args, \"\", \"--debug-audit\"),\n // A bare `--trace` with no path is still a request to trace, so it gets\n // the default file rather than being read as \"no trace\".\n tracePath: args.includes(\"--trace\")\n ? (getArgValue(args, \"\", \"--trace\") ?? DEFAULT_TRACE_FILE)\n : getArgValue(args, \"\", \"--trace\"),\n };\n}\n\n/**\n * Which subcommand form was used.\n *\n * `al audit <url>`, `al <url>` and a bare `al` with a config file all reach the\n * same scan; anything starting with `-` is a flag, never a URL.\n */\nexport function resolveCommand(args: string[]): { action: \"help\" | \"audit\"; url?: string } {\n const command = args[0];\n if (!command || command === \"-h\" || command === \"--help\") return { action: \"help\" };\n if (command === \"audit\") return { action: \"audit\", url: args[1] };\n if (!command.startsWith(\"-\")) return { action: \"audit\", url: command };\n return { action: \"audit\" };\n}\n\n/**\n * Per-category thresholds, from `--assert-category id:min` and the config file.\n *\n * The flag repeats, so this cannot go through `getArgValue`, which returns the\n * first occurrence only. A fresh object is returned rather than the config\n * file's own: merging into `fileConfig.assertCategories` mutated the loaded\n * config, which the caller may still read.\n */\nexport function parseCategoryAssertions(\n args: string[],\n fileConfig: FileConfig & { assertCategories?: Record<string, number> } = {},\n): Record<string, number> {\n const out: Record<string, number> = { ...(fileConfig.assertCategories ?? {}) };\n\n const record = (pair: string | undefined) => {\n if (!pair) return;\n const [catId, min] = pair.split(\":\");\n if (catId && min) out[catId] = Number(min);\n };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n if (arg.startsWith(\"--assert-category=\")) record(arg.slice(\"--assert-category=\".length));\n else if (arg === \"--assert-category\") record(args[i + 1]);\n }\n return out;\n}\n\n/** A category as the assertions see it. */\nexport interface AssertableCategory {\n id: string;\n name: string;\n score: number;\n}\n\nexport interface FailedAssertion {\n name: string;\n score: number;\n threshold: number;\n}\n\n/**\n * The first assertion the scan does not meet, or undefined if it meets all.\n *\n * A threshold naming a category that did not run is not a failure: `--preset`\n * and `--categories` both narrow the scan, and failing CI over a category the\n * operator deliberately excluded would make the two flags unusable together.\n */\nexport function failedAssertion(\n categories: AssertableCategory[],\n assertions: Record<string, number>,\n): FailedAssertion | undefined {\n for (const [catId, threshold] of Object.entries(assertions)) {\n const matched = categories.find(\n (c) => c.id === catId || c.name.toLowerCase().includes(catId.toLowerCase()),\n );\n if (matched && matched.score < threshold) {\n return { name: matched.name, score: matched.score, threshold };\n }\n }\n return undefined;\n}\n\n/** A check as the debugger selects it. */\nexport interface DebuggableCheck {\n id: string;\n title: string;\n status: string;\n}\n\n/**\n * The checks `--debug-audit` should print.\n *\n * `fails` is a reserved value meaning \"everything that is not clean\"; anything\n * else matches an audit id exactly or a title substring, so an operator can\n * type `faqpage` instead of the full id.\n */\nexport function selectDebugChecks<T extends DebuggableCheck>(checks: T[], debugAudit: string): T[] {\n if (debugAudit === \"fails\") {\n return checks.filter((c) => c.status === \"fail\" || c.status === \"warn\");\n }\n const needle = debugAudit.toLowerCase();\n return checks.filter((c) => c.id === debugAudit || c.title.toLowerCase().includes(needle));\n}\n\n/** The shell command that opens a file in the platform's default application. */\nexport function openCommand(platform: NodeJS.Platform, filePath: string): string {\n if (platform === \"darwin\") return `open \"${filePath}\"`;\n if (platform === \"win32\") return `start \"\" \"${filePath}\"`;\n return `xdg-open \"${filePath}\"`;\n}\n","import type { AuditTier } from \"@forkpoint/agent-lighthouse-core\";\n\n/**\n * A check whose tier is not `scored` is reported but never moves a score.\n * Without a marker, a failing advisory reads as work the operator owes.\n */\nexport function tierMarker(tier?: AuditTier): string {\n if (tier === \"informative\") return \" \\x1b[36m(advisory)\\x1b[0m\";\n if (tier === \"experimental\") return \" \\x1b[36m(experimental)\\x1b[0m\";\n return \"\";\n}\n"],"mappings":"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","names":["import_agent_lighthouse_core","args"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forkpoint/agent-lighthouse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Lighthouse-style CLI for auditing websites for AI agents, LLM crawlers, MCP clients, llms.txt, WebMCP, OpenAPI, and Schema.org readiness",
|
|
5
5
|
"author": "ForkPoint",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@forkpoint/agent-lighthouse-core": "0.
|
|
51
|
-
"@forkpoint/agent-lighthouse-report": "0.
|
|
50
|
+
"@forkpoint/agent-lighthouse-core": "2.0.0",
|
|
51
|
+
"@forkpoint/agent-lighthouse-report": "2.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^22.10.5",
|