UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

138 lines • 4.81 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import path from 'node:path'; import { snapshot, navigation, generateReport, zod, } from '../third_party/index.js'; import { ToolCategory } from './categories.js'; import { startTrace } from './performance.js'; import { definePageTool } from './ToolDefinition.js'; export const lighthouseAudit = definePageTool({ name: 'lighthouse_audit', description: `Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run ${startTrace.name}`, annotations: { category: ToolCategory.DEBUGGING, readOnlyHint: false, }, schema: { mode: zod .enum(['navigation', 'snapshot']) .default('navigation') .describe('"navigation" reloads & audits. "snapshot" analyzes current state.'), device: zod .enum(['desktop', 'mobile']) .default('desktop') .describe('Device to emulate.'), outputDirPath: zod .string() .optional() .describe('Directory for reports. If omitted, uses temporary files.'), }, blockedByDialog: true, verifyFilesSchema: { outputDirPath: true, }, handler: async (request, response, context) => { const page = request.page; const categories = [ 'accessibility', 'seo', 'best-practices', 'agentic-browsing', ]; const formats = ['json', 'html']; const { mode = 'navigation', device = 'desktop', outputDirPath, } = request.params; const flags = { onlyCategories: categories, output: formats, // Default 30 second timeout for page load. maxWaitForLoad: 30_000, }; if (device === 'desktop') { flags.formFactor = 'desktop'; flags.screenEmulation = { mobile: false, width: 1350, height: 940, deviceScaleFactor: 1, disabled: false, }; } else { flags.formFactor = 'mobile'; flags.screenEmulation = { mobile: true, width: 412, height: 823, deviceScaleFactor: 1.75, disabled: false, }; } let result; try { if (mode === 'navigation') { result = await navigation(page.pptrPage, page.pptrPage.url(), { flags, }); } else { result = await snapshot(page.pptrPage, { flags, }); } if (!result) { throw new Error('Lighthouse audit failed.'); } } finally { await page.restoreEmulation(); } const lhr = result.lhr; const reportPaths = []; const encoder = new TextEncoder(); const savePromises = formats.map(async (format) => { const report = generateReport(lhr, format); const data = encoder.encode(report); if (outputDirPath) { const reportPath = path.join(outputDirPath, `report`); const { filename } = await context.saveFile(data, reportPath, `.${format}`); return filename; } const { filepath } = await context.saveTemporaryFile(data, `report.${format}`); return filepath; }); const results = await Promise.allSettled(savePromises); for (const res of results) { if (res.status === 'rejected') { throw res.reason; } reportPaths.push(res.value); } const categoryScores = Object.values(lhr.categories).map(c => ({ id: c.id, title: c.title, score: c.score, })); const failedAudits = Object.values(lhr.audits).filter(a => a.score !== null && a.score < 1).length; const passedAudits = Object.values(lhr.audits).filter(a => a.score === 1).length; const output = { summary: { mode, device, url: lhr.mainDocumentUrl, scores: categoryScores, audits: { failed: failedAudits, passed: passedAudits, }, timing: { total: lhr.timing.total, }, }, reports: reportPaths, }; response.attachLighthouseResult(output); }, }); //# sourceMappingURL=lighthouse.js.map