diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index db816d2..12bb2dc 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -1,5 +1,11 @@ # mycoder-agent +## 0.3.0 + +### Minor Changes + +- Better browser experience: show browser, take over user session, content filter, robustness improvements to browsing. + ## 0.2.1 ### Patch Changes diff --git a/packages/agent/package.json b/packages/agent/package.json index 75f3195..16de755 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "mycoder-agent", - "version": "0.2.1", + "version": "0.3.0", "description": "Agent module for mycoder - an AI-powered software development assistant", "type": "module", "main": "dist/index.js", @@ -45,10 +45,12 @@ "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.37", + "@mozilla/readability": "^0.5.0", "@playwright/test": "^1.50.1", "@vitest/browser": "^3.0.5", "chalk": "^5", "dotenv": "^16", + "jsdom": "^26.0.0", "playwright": "^1.50.1", "uuid": "^11", "zod": "^3", diff --git a/packages/agent/src/core/executeToolCall.ts b/packages/agent/src/core/executeToolCall.ts index 3c7dab4..0de1cdf 100644 --- a/packages/agent/src/core/executeToolCall.ts +++ b/packages/agent/src/core/executeToolCall.ts @@ -23,7 +23,7 @@ export const executeToolCall = async ( customPrefix: tool.logPrefix, }); - const toolContext = { + const toolContext: ToolContext = { ...context, logger, }; diff --git a/packages/agent/src/core/toolAgent.respawn.test.ts b/packages/agent/src/core/toolAgent.respawn.test.ts index d857732..be5c49a 100644 --- a/packages/agent/src/core/toolAgent.respawn.test.ts +++ b/packages/agent/src/core/toolAgent.respawn.test.ts @@ -5,11 +5,14 @@ import { getTools } from '../../src/tools/getTools.js'; import { MockLogger } from '../utils/mockLogger.js'; import { TokenTracker } from './tokens.js'; +import { ToolContext } from './types.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; // Mock Anthropic SDK diff --git a/packages/agent/src/core/toolAgent.test.ts b/packages/agent/src/core/toolAgent.test.ts index 26f2af5..43bf116 100644 --- a/packages/agent/src/core/toolAgent.test.ts +++ b/packages/agent/src/core/toolAgent.test.ts @@ -5,12 +5,14 @@ import { MockLogger } from '../utils/mockLogger.js'; import { executeToolCall } from './executeToolCall.js'; import { TokenTracker } from './tokens.js'; import { toolAgent } from './toolAgent.js'; -import { Tool } from './types.js'; +import { Tool, ToolContext } from './types.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/core/types.ts b/packages/agent/src/core/types.ts index efa2c27..328f146 100644 --- a/packages/agent/src/core/types.ts +++ b/packages/agent/src/core/types.ts @@ -6,10 +6,14 @@ import { TokenTracker } from './tokens.js'; export type TokenLevel = 'debug' | 'verbose' | 'info' | 'warn' | 'error'; +export type pageFilter = 'simple' | 'none' | 'readability'; + export type ToolContext = { logger: Logger; workingDirectory: string; headless: boolean; + userSession: boolean; + pageFilter: pageFilter; tokenTracker: TokenTracker; }; diff --git a/packages/agent/src/tools/browser/browseMessage.ts b/packages/agent/src/tools/browser/browseMessage.ts index bd89fcc..49547da 100644 --- a/packages/agent/src/tools/browser/browseMessage.ts +++ b/packages/agent/src/tools/browser/browseMessage.ts @@ -3,7 +3,9 @@ import { zodToJsonSchema } from 'zod-to-json-schema'; import { Tool } from '../../core/types.js'; import { errorToString } from '../../utils/errorToString.js'; +import { sleep } from '../../utils/sleep.js'; +import { filterPageContent } from './filterPageContent.js'; import { browserSessions, type BrowserAction, SelectorType } from './types.js'; // Schema for browser action @@ -71,8 +73,29 @@ export const browseMessageTool: Tool = { parameters: zodToJsonSchema(parameterSchema), returns: zodToJsonSchema(returnSchema), - execute: async ({ instanceId, action }, { logger }): Promise => { + execute: async ( + { instanceId, action }, + { logger, pageFilter }, + ): Promise => { + // Validate action format + if (!action || typeof action !== 'object') { + logger.error('Invalid action format: action must be an object'); + return { + status: 'error', + error: 'Invalid action format: action must be an object', + }; + } + + if (!action.actionType) { + logger.error('Invalid action format: actionType is required'); + return { + status: 'error', + error: 'Invalid action format: actionType is required', + }; + } + logger.verbose(`Executing browser action: ${action.actionType}`); + logger.verbose(`Webpage processing mode: ${pageFilter}`); try { const session = browserSessions.get(instanceId); @@ -87,10 +110,44 @@ export const browseMessageTool: Tool = { if (!action.url) { throw new Error('URL required for goto action'); } - await page.goto(action.url, { waitUntil: 'networkidle' }); - const content = await page.content(); - logger.verbose('Navigation completed successfully'); - return { status: 'success', content }; + + try { + // Try with 'domcontentloaded' first which is more reliable than 'networkidle' + logger.verbose( + `Navigating to ${action.url} with 'domcontentloaded' waitUntil`, + ); + await page.goto(action.url, { waitUntil: 'domcontentloaded' }); + await sleep(3000); + const content = await filterPageContent(page, pageFilter); + logger.verbose(`Content: ${content}`); + logger.verbose( + 'Navigation completed with domcontentloaded strategy', + ); + logger.verbose(`Content length: ${content.length} characters`); + return { status: 'success', content }; + } catch (navError) { + // If that fails, try with no waitUntil option + logger.warn( + `Failed with domcontentloaded strategy: ${errorToString(navError)}`, + ); + logger.verbose( + `Retrying navigation to ${action.url} with no waitUntil option`, + ); + + try { + await page.goto(action.url); + await sleep(3000); + const content = await filterPageContent(page, pageFilter); + logger.verbose(`Content: ${content}`); + logger.verbose('Navigation completed with basic strategy'); + return { status: 'success', content }; + } catch (innerError) { + logger.error( + `Failed with basic navigation strategy: ${errorToString(innerError)}`, + ); + throw innerError; // Re-throw to be caught by outer catch block + } + } } case 'click': { @@ -102,7 +159,8 @@ export const browseMessageTool: Tool = { action.selectorType, ); await page.click(clickSelector); - const content = await page.content(); + await sleep(1000); // Wait for any content changes after click + const content = await filterPageContent(page, pageFilter); logger.verbose( `Click action completed on selector: ${clickSelector}`, ); @@ -136,8 +194,9 @@ export const browseMessageTool: Tool = { } case 'content': { - const content = await page.content(); + const content = await filterPageContent(page, pageFilter); logger.verbose('Page content retrieved successfully'); + logger.verbose(`Content length: ${content.length} characters`); return { status: 'success', content }; } @@ -164,9 +223,12 @@ export const browseMessageTool: Tool = { } }, - logParameters: ({ action, description }, { logger }) => { + logParameters: ( + { action, description }, + { logger, pageFilter = 'simple' }, + ) => { logger.info( - `Performing browser action: ${action.actionType}, ${description}`, + `Performing browser action: ${action.actionType} with ${pageFilter} processing, ${description}`, ); }, diff --git a/packages/agent/src/tools/browser/browseStart.ts b/packages/agent/src/tools/browser/browseStart.ts index d1d2ff3..a4c5fa6 100644 --- a/packages/agent/src/tools/browser/browseStart.ts +++ b/packages/agent/src/tools/browser/browseStart.ts @@ -5,12 +5,13 @@ import { zodToJsonSchema } from 'zod-to-json-schema'; import { Tool } from '../../core/types.js'; import { errorToString } from '../../utils/errorToString.js'; +import { sleep } from '../../utils/sleep.js'; +import { filterPageContent } from './filterPageContent.js'; import { browserSessions } from './types.js'; const parameterSchema = z.object({ url: z.string().url().optional().describe('Initial URL to navigate to'), - timeout: z .number() .optional() @@ -40,23 +41,37 @@ export const browseStartTool: Tool = { execute: async ( { url, timeout = 30000 }, - { logger, headless = true }, + { logger, headless, userSession, pageFilter }, ): Promise => { logger.verbose(`Starting browser session${url ? ` at ${url}` : ''}`); + logger.verbose( + `User session mode: ${userSession ? 'enabled' : 'disabled'}`, + ); + logger.verbose(`Webpage processing mode: ${pageFilter}`); try { const instanceId = uuidv4(); // Launch browser - const browser = await chromium.launch({ + const launchOptions = { headless, - }); + }; + + // Use system Chrome installation if userSession is true + if (userSession) { + logger.verbose('Using system Chrome installation'); + // For Chrome, we use the channel option to specify Chrome + launchOptions['channel'] = 'chrome'; + } + + const browser = await chromium.launch(launchOptions); // Create new context with default settings const context = await browser.newContext({ viewport: null, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + serviceWorkers: 'block', // Block service workers which can cause continuous network activity }); // Create new page @@ -80,11 +95,42 @@ export const browseStartTool: Tool = { // Navigate to URL if provided let content = ''; if (url) { - await page.goto(url, { waitUntil: 'networkidle' }); - content = await page.content(); + try { + // Try with 'domcontentloaded' first which is more reliable than 'networkidle' + logger.verbose( + `Navigating to ${url} with 'domcontentloaded' waitUntil`, + ); + await page.goto(url, { waitUntil: 'domcontentloaded', timeout }); + await sleep(3000); + content = await filterPageContent(page, pageFilter); + logger.verbose(`Content: ${content}`); + logger.verbose('Navigation completed with domcontentloaded strategy'); + } catch (error) { + // If that fails, try with no waitUntil option at all (most basic) + logger.warn( + `Failed with domcontentloaded strategy: ${errorToString(error)}`, + ); + logger.verbose( + `Retrying navigation to ${url} with no waitUntil option`, + ); + + try { + await page.goto(url, { timeout }); + await sleep(3000); + content = await filterPageContent(page, pageFilter); + logger.verbose(`Content: ${content}`); + logger.verbose('Navigation completed with basic strategy'); + } catch (innerError) { + logger.error( + `Failed with basic navigation strategy: ${errorToString(innerError)}`, + ); + throw innerError; // Re-throw to be caught by outer catch block + } + } } logger.verbose('Browser session started successfully'); + logger.verbose(`Content length: ${content.length} characters`); return { instanceId, @@ -101,9 +147,9 @@ export const browseStartTool: Tool = { } }, - logParameters: ({ url, description }, { logger }) => { + logParameters: ({ url, description }, { logger, pageFilter = 'simple' }) => { logger.info( - `Starting browser session${url ? ` at ${url}` : ''}, ${description}`, + `Starting browser session${url ? ` at ${url}` : ''} with ${pageFilter} processing, ${description}`, ); }, diff --git a/packages/agent/src/tools/browser/filterPageContent.ts b/packages/agent/src/tools/browser/filterPageContent.ts new file mode 100644 index 0000000..a1d3879 --- /dev/null +++ b/packages/agent/src/tools/browser/filterPageContent.ts @@ -0,0 +1,112 @@ +import { Readability } from '@mozilla/readability'; +import { JSDOM } from 'jsdom'; +import { Page } from 'playwright'; + +/** + * Returns the raw HTML content of the page without any processing + */ +async function getNoneProcessedDOM(page: Page): Promise { + return await page.content(); +} + +/** + * Processes the page using Mozilla's Readability to extract the main content + * Falls back to simple processing if Readability fails + */ +async function getReadabilityProcessedDOM(page: Page): Promise { + try { + const html = await page.content(); + const url = page.url(); + const dom = new JSDOM(html, { url }); + const reader = new Readability(dom.window.document); + const article = reader.parse(); + + if (!article) { + console.warn( + 'Readability could not parse the page, falling back to simple mode', + ); + return getSimpleProcessedDOM(page); + } + + // Return a formatted version of the article + return JSON.stringify( + { + url: url, + title: article.title || '', + content: article.content || '', + textContent: article.textContent || '', + excerpt: article.excerpt || '', + byline: article.byline || '', + dir: article.dir || '', + siteName: article.siteName || '', + length: article.length || 0, + }, + null, + 2, + ); + } catch (error) { + console.error('Error using Readability:', error); + // Fallback to simple mode if Readability fails + return getSimpleProcessedDOM(page); + } +} + +/** + * Processes the page by removing invisible elements and non-visual tags + */ +async function getSimpleProcessedDOM(page: Page): Promise { + const domContent = await page.evaluate(() => { + const clone = document.documentElement; + + const elements = clone.querySelectorAll('*'); + + const elementsToRemove: Element[] = []; + elements.forEach((element) => { + const computedStyle = window.getComputedStyle(element); + const isVisible = + computedStyle.display !== 'none' && + computedStyle.visibility !== 'hidden' && + computedStyle.opacity !== '0'; + + if (!isVisible) { + elementsToRemove.push(element); + } + }); + + const nonVisualTags = clone.querySelectorAll( + 'noscript, iframe, link[rel="stylesheet"], meta, svg, img, symbol, path, style, script', + ); + nonVisualTags.forEach((element) => elementsToRemove.push(element)); + + elementsToRemove.forEach((element) => element.remove()); + + console.log( + 'removing ', + elementsToRemove.length, + ' elements out of a total ', + elements.length, + ); + + return clone.outerHTML; + }); + + return domContent.replace(/\n/g, '').replace(/\s+/g, ' '); +} + +/** + * Gets the rendered DOM of a page with specified processing method + */ +export async function filterPageContent( + page: Page, + pageFilter: 'simple' | 'none' | 'readability', +): Promise { + switch (pageFilter) { + case 'none': + return getNoneProcessedDOM(page); + case 'readability': + return getReadabilityProcessedDOM(page); + case 'simple': + default: + return getSimpleProcessedDOM(page); + } +} diff --git a/packages/agent/src/tools/io/readFile.test.ts b/packages/agent/src/tools/io/readFile.test.ts index cfb68a7..71845bd 100644 --- a/packages/agent/src/tools/io/readFile.test.ts +++ b/packages/agent/src/tools/io/readFile.test.ts @@ -1,14 +1,17 @@ import { describe, it, expect } from 'vitest'; import { TokenTracker } from '../../core/tokens.js'; +import { ToolContext } from '../../core/types.js'; import { MockLogger } from '../../utils/mockLogger'; import { readFileTool } from './readFile.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/tools/io/updateFile.test.ts b/packages/agent/src/tools/io/updateFile.test.ts index ed79a83..d6295e4 100644 --- a/packages/agent/src/tools/io/updateFile.test.ts +++ b/packages/agent/src/tools/io/updateFile.test.ts @@ -6,16 +6,19 @@ import { join } from 'path'; import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { TokenTracker } from '../../core/tokens.js'; +import { ToolContext } from '../../core/types.js'; import { MockLogger } from '../../utils/mockLogger.js'; import { shellExecuteTool } from '../system/shellExecute.js'; import { readFileTool } from './readFile.js'; import { updateFileTool } from './updateFile.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/tools/system/respawn.test.ts b/packages/agent/src/tools/system/respawn.test.ts index 7e0431f..0d80cfd 100644 --- a/packages/agent/src/tools/system/respawn.test.ts +++ b/packages/agent/src/tools/system/respawn.test.ts @@ -1,14 +1,17 @@ import { describe, it, expect } from 'vitest'; import { TokenTracker } from '../../core/tokens'; +import { ToolContext } from '../../core/types'; import { MockLogger } from '../../utils/mockLogger'; import { respawnTool } from './respawn'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; describe('respawnTool', () => { diff --git a/packages/agent/src/tools/system/shellExecute.test.ts b/packages/agent/src/tools/system/shellExecute.test.ts index dddfc0a..5670f3e 100644 --- a/packages/agent/src/tools/system/shellExecute.test.ts +++ b/packages/agent/src/tools/system/shellExecute.test.ts @@ -1,14 +1,17 @@ import { describe, it, expect } from 'vitest'; import { TokenTracker } from '../../core/tokens.js'; +import { ToolContext } from '../../core/types.js'; import { MockLogger } from '../../utils/mockLogger.js'; import { shellExecuteTool } from './shellExecute.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/tools/system/shellMessage.test.ts b/packages/agent/src/tools/system/shellMessage.test.ts index 390918b..4f89457 100644 --- a/packages/agent/src/tools/system/shellMessage.test.ts +++ b/packages/agent/src/tools/system/shellMessage.test.ts @@ -1,16 +1,19 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { TokenTracker } from '../../core/tokens.js'; +import { ToolContext } from '../../core/types.js'; import { MockLogger } from '../../utils/mockLogger.js'; import { sleep } from '../../utils/sleep.js'; import { shellMessageTool, NodeSignals } from './shellMessage.js'; import { processStates, shellStartTool } from './shellStart.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/tools/system/shellStart.test.ts b/packages/agent/src/tools/system/shellStart.test.ts index 72d0f33..88a0764 100644 --- a/packages/agent/src/tools/system/shellStart.test.ts +++ b/packages/agent/src/tools/system/shellStart.test.ts @@ -1,15 +1,18 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { TokenTracker } from '../../core/tokens.js'; +import { ToolContext } from '../../core/types.js'; import { MockLogger } from '../../utils/mockLogger.js'; import { sleep } from '../../utils/sleep.js'; import { processStates, shellStartTool } from './shellStart.js'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; describe('shellStartTool', () => { diff --git a/packages/agent/src/tools/system/sleep.test.ts b/packages/agent/src/tools/system/sleep.test.ts index 514e774..725fe68 100644 --- a/packages/agent/src/tools/system/sleep.test.ts +++ b/packages/agent/src/tools/system/sleep.test.ts @@ -1,14 +1,17 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { TokenTracker } from '../../core/tokens'; +import { ToolContext } from '../../core/types'; import { MockLogger } from '../../utils/mockLogger'; import { sleepTool } from './sleep'; -const toolContext = { +const toolContext: ToolContext = { logger: new MockLogger(), headless: true, workingDirectory: '.', + userSession: false, + pageFilter: 'simple', tokenTracker: new TokenTracker(), }; diff --git a/packages/agent/src/utils/userPrompt.ts b/packages/agent/src/utils/userPrompt.ts index 3da50da..3cf1cfe 100644 --- a/packages/agent/src/utils/userPrompt.ts +++ b/packages/agent/src/utils/userPrompt.ts @@ -9,7 +9,11 @@ export const userPrompt = async (prompt: string): Promise => { }); try { - return await rl.question(chalk.green('\n' + prompt + '\n') + '\n> '); + const result = await rl.question( + chalk.green('\n' + prompt + '\n') + '\n> ', + ); + console.log(); + return result; } finally { rl.close(); } diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6c6f5c9..f8d149a 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # mycoder +## 0.3.0 + +### Minor Changes + +- Better browser experience: show browser, take over user session, content filter, robustness improvements to browsing. + +### Patch Changes + +- Updated dependencies + - mycoder-agent@0.3.0 + ## 0.2.3 ### Patch Changes diff --git a/packages/cli/README.md b/packages/cli/README.md index c1c1a1d..1c7e5f6 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -54,6 +54,8 @@ mycoder --promptFile=your-prompt.txt - `-f, --file`: Read prompt from a specified file - `--log`: Set log level (info, verbose, warn, error) - `--tokenUsage`: Output token usage at info log level +- `--headless`: Use browser in headless mode with no UI showing (default: true) +- `--userSession`: Use user's existing browser session instead of sandboxed session (default: false) - `-h, --help`: Show help - `-V, --version`: Show version diff --git a/packages/cli/package.json b/packages/cli/package.json index 289d490..1cf9a13 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "mycoder", "description": "A command line tool using agent that can do arbitrary tasks, including coding tasks", - "version": "0.2.3", + "version": "0.3.0", "type": "module", "bin": "./bin/cli.js", "main": "./dist/index.js", diff --git a/packages/cli/src/commands/$default.ts b/packages/cli/src/commands/$default.ts index 7a78ead..b2ea60e 100644 --- a/packages/cli/src/commands/$default.ts +++ b/packages/cli/src/commands/$default.ts @@ -9,6 +9,7 @@ import { getAnthropicApiKeyError, userPrompt, LogLevel, + subAgentTool, } from 'mycoder-agent'; import { TokenTracker } from 'mycoder-agent/dist/core/tokens.js'; @@ -36,6 +37,7 @@ export const command: CommandModule = { const logger = new Logger({ name: 'Default', logLevel: nameToLogIndex(argv.logLevel), + customPrefix: subAgentTool.logPrefix, }); const packageInfo = getPackageInfo(); @@ -128,6 +130,8 @@ export const command: CommandModule = { const result = await toolAgent(prompt, tools, undefined, { logger, headless: argv.headless ?? true, + userSession: argv.userSession ?? false, + pageFilter: argv.pageFilter ?? 'none', workingDirectory: '.', tokenTracker, }); diff --git a/packages/cli/src/options.ts b/packages/cli/src/options.ts index 988e462..9e4827e 100644 --- a/packages/cli/src/options.ts +++ b/packages/cli/src/options.ts @@ -4,6 +4,8 @@ export type SharedOptions = { readonly file?: string; readonly tokenUsage?: boolean; readonly headless?: boolean; + readonly userSession?: boolean; + readonly pageFilter?: 'simple' | 'none' | 'readability'; }; export const sharedOptions = { @@ -35,4 +37,16 @@ export const sharedOptions = { description: 'Use browser in headless mode with no UI showing', default: true, } as const, + userSession: { + type: 'boolean', + description: + "Use user's existing browser session instead of sandboxed session", + default: false, + } as const, + pageFilter: { + type: 'string', + description: 'Method to process webpage content', + default: 'none', + choices: ['simple', 'none', 'readability'], + } as const, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95d40fb..e9dea86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,6 +57,9 @@ importers: '@anthropic-ai/sdk': specifier: ^0.37 version: 0.37.0 + '@mozilla/readability': + specifier: ^0.5.0 + version: 0.5.0 '@playwright/test': specifier: ^1.50.1 version: 1.50.1 @@ -69,6 +72,9 @@ importers: dotenv: specifier: ^16 version: 16.4.7 + jsdom: + specifier: ^26.0.0 + version: 26.0.0 playwright: specifier: ^1.50.1 version: 1.50.1 @@ -99,7 +105,7 @@ importers: version: 5.7.3 vitest: specifier: ^3 - version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(jsdom@26.0.0)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) packages/cli: dependencies: @@ -154,13 +160,16 @@ importers: version: 5.7.3 vitest: specifier: ^3 - version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + version: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(jsdom@26.0.0)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) packages: '@anthropic-ai/sdk@0.37.0': resolution: {integrity: sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==} + '@asamuzakjp/css-color@2.8.3': + resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -237,6 +246,34 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@csstools/color-helpers@5.0.2': + resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.2': + resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-color-parser@3.0.8': + resolution: {integrity: sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} @@ -653,6 +690,10 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@mozilla/readability@0.5.0': + resolution: {integrity: sha512-Z+CZ3QaosfFaTqvhQsIktyGrjFjSC0Fa4EMph4mqKnWhmyoGICsV/8QK+8HpXut6zV7zwfWwqDmEjtk1Qf6EgQ==} + engines: {node: '>=14.0.0'} + '@mswjs/interceptors@0.37.6': resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} engines: {node: '>=18'} @@ -959,6 +1000,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -1142,6 +1187,14 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + cssstyle@4.2.1: + resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==} + engines: {node: '>=18'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1171,6 +1224,9 @@ packages: supports-color: optional: true + decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1234,6 +1290,10 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + es-abstract@1.23.9: resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} @@ -1619,6 +1679,18 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-id@4.1.1: resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} hasBin: true @@ -1630,6 +1702,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1716,6 +1792,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1788,6 +1867,15 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsdom@26.0.0: + resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1926,6 +2014,9 @@ packages: encoding: optional: true + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -2006,6 +2097,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2166,6 +2260,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2184,6 +2281,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2321,6 +2422,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.9.2: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2360,6 +2464,13 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@6.1.79: + resolution: {integrity: sha512-HM+Ud/2oQuHt4I43Nvjc213Zji/z25NSH5OkJskJwHXNtYh9DTRlHMDFhms9dFMP7qyve/yVaXFIxmcJ7TdOjw==} + + tldts@6.1.79: + resolution: {integrity: sha512-wjlYwK8lC/WcywLWf3A7qbK07SexezXjTRVwuPWXHvcjD7MnpPS2RXY5rLO3g12a8CNc7Y7jQRQsV7XyuBZjig==} + hasBin: true + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -2376,9 +2487,17 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} + tough-cookie@5.1.1: + resolution: {integrity: sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + ts-api-utils@2.0.1: resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} engines: {node: '>=18.12'} @@ -2537,6 +2656,10 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -2544,6 +2667,22 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.1.1: + resolution: {integrity: sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -2601,6 +2740,13 @@ packages: utf-8-validate: optional: true + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2654,6 +2800,14 @@ snapshots: transitivePeerDependencies: - encoding + '@asamuzakjp/css-color@2.8.3': + dependencies: + '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + lru-cache: 10.4.3 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -2821,6 +2975,26 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 + '@csstools/color-helpers@5.0.2': {} + + '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/color-helpers': 5.0.2 + '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + '@esbuild/aix-ppc64@0.24.2': optional: true @@ -3104,6 +3278,8 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@mozilla/readability@0.5.0': {} + '@mswjs/interceptors@0.37.6': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -3353,7 +3529,7 @@ snapshots: msw: 2.7.3(@types/node@18.19.76)(typescript@5.7.3) sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + vitest: 3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(jsdom@26.0.0)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) ws: 8.18.1 optionalDependencies: playwright: 1.50.1 @@ -3415,6 +3591,8 @@ snapshots: acorn@8.14.0: {} + agent-base@7.1.3: {} + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -3607,6 +3785,16 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cssstyle@4.2.1: + dependencies: + '@asamuzakjp/css-color': 2.8.3 + rrweb-cssom: 0.8.0 + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.1 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.3 @@ -3633,6 +3821,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.5.0: {} + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -3689,6 +3879,8 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + entities@4.5.0: {} + es-abstract@1.23.9: dependencies: array-buffer-byte-length: 1.0.2 @@ -4211,6 +4403,24 @@ snapshots: headers-polyfill@4.0.3: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + human-id@4.1.1: {} humanize-ms@1.2.1: @@ -4221,6 +4431,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} import-fresh@3.3.1: @@ -4310,6 +4524,8 @@ snapshots: is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.3 @@ -4383,6 +4599,34 @@ snapshots: dependencies: argparse: 2.0.1 + jsdom@26.0.0: + dependencies: + cssstyle: 4.2.1 + data-urls: 5.0.0 + decimal.js: 10.5.0 + form-data: 4.0.2 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.16 + parse5: 7.2.1 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.1 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.1 + ws: 8.18.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -4504,6 +4748,8 @@ snapshots: dependencies: whatwg-url: 5.0.0 + nwsapi@2.2.16: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -4590,6 +4836,10 @@ snapshots: dependencies: callsites: 3.1.0 + parse5@7.2.1: + dependencies: + entities: 4.5.0 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4747,6 +4997,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.34.8 fsevents: 2.3.3 + rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -4772,6 +5024,10 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + semver@6.3.1: {} semver@7.7.1: {} @@ -4923,6 +5179,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + synckit@0.9.2: dependencies: '@pkgr/core': 0.1.1 @@ -4955,6 +5213,12 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@6.1.79: {} + + tldts@6.1.79: + dependencies: + tldts-core: 6.1.79 + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -4972,8 +5236,16 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 + tough-cookie@5.1.1: + dependencies: + tldts: 6.1.79 + tr46@0.0.3: {} + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.0.1(typescript@5.7.3): dependencies: typescript: 5.7.3 @@ -5108,7 +5380,7 @@ snapshots: tsx: 4.19.3 yaml: 2.7.0 - vitest@3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): + vitest@3.0.6(@types/debug@4.1.12)(@types/node@18.19.76)(@vitest/browser@3.0.6)(jiti@2.4.2)(jsdom@26.0.0)(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): dependencies: '@vitest/expect': 3.0.6 '@vitest/mocker': 3.0.6(msw@2.7.3(@types/node@18.19.76)(typescript@5.7.3))(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)) @@ -5134,6 +5406,7 @@ snapshots: '@types/debug': 4.1.12 '@types/node': 18.19.76 '@vitest/browser': 3.0.6(@types/node@18.19.76)(playwright@1.50.1)(typescript@5.7.3)(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vitest@3.0.6) + jsdom: 26.0.0 transitivePeerDependencies: - jiti - less @@ -5148,10 +5421,27 @@ snapshots: - tsx - yaml + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.1.1: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -5228,6 +5518,10 @@ snapshots: ws@8.18.1: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + y18n@5.0.8: {} yaml@2.7.0: