clean up deps: remove unused, update zod v4, replace chalk with picocolors

This commit is contained in:
Tommy D. Rossi
2026-01-15 13:32:54 +01:00
parent 423365caae
commit cda69383f0
3 changed files with 307 additions and 293 deletions
+4 -6
View File
@@ -32,6 +32,7 @@
"@types/ws": "^8.18.1",
"@vitest/ui": "^4.0.8",
"bippy": "^0.5.27",
"devtools-protocol": "^0.0.1568893",
"image-size": "^2.0.2",
"mcp-extension": "workspace:*",
"vite-node": "^5.0.0",
@@ -40,22 +41,19 @@
"dependencies": {
"@hono/node-server": "^1.19.6",
"@hono/node-ws": "^1.2.0",
"@modelcontextprotocol/sdk": "^1.21.1",
"@modelcontextprotocol/sdk": "^1.25.2",
"cac": "^6.7.14",
"chalk": "^5.6.2",
"colord": "^2.9.3",
"devtools-protocol": "^0.0.1543509",
"diff": "^8.0.2",
"hono": "^4.10.6",
"kill-port-process": "^3.2.1",
"picocolors": "^1.1.1",
"playwright-core": "^1.56.1",
"posthtml": "^0.16.7",
"posthtml-beautify": "^0.7.0",
"string-dedent": "^3.0.2",
"strip-ansi": "^7.1.2",
"user-agents": "^1.1.669",
"ws": "^8.18.3",
"zod": "^3"
"zod": "^4.3.5"
},
"optionalDependencies": {
"sharp": "^0.34.5"
+34 -34
View File
@@ -6,7 +6,7 @@ import type { WSContext } from 'hono/ws'
import type { Protocol } from './cdp-types.js'
import type { CDPCommand, CDPResponseBase, CDPEventBase, CDPEventFor, RelayServerEvents } from './cdp-types.js'
import type { ExtensionMessage, ExtensionEventMessage } from './protocol.js'
import chalk from 'chalk'
import pc from 'picocolors'
import { EventEmitter } from 'node:events'
import { VERSION } from './utils.js'
@@ -150,17 +150,17 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
}
const detailsStr = details.length > 0 ? ` ${chalk.gray(details.join(', '))}` : ''
const detailsStr = details.length > 0 ? ` ${pc.gray(details.join(', '))}` : ''
if (direction === 'from-playwright') {
const clientLabel = clientId ? chalk.blue(`[${clientId}]`) : ''
logger?.log(chalk.cyan('← Playwright'), clientLabel + ':', method + detailsStr)
const clientLabel = clientId ? pc.blue(`[${clientId}]`) : ''
logger?.log(pc.cyan('← Playwright'), clientLabel + ':', method + detailsStr)
} else if (direction === 'from-extension') {
logger?.log(chalk.yellow('← Extension:'), method + detailsStr)
logger?.log(pc.yellow('← Extension:'), method + detailsStr)
} else if (direction === 'to-playwright') {
const color = source === 'server' ? chalk.magenta : chalk.green
const sourceLabel = source === 'server' ? chalk.gray(' (server-generated)') : ''
const clientLabel = clientId ? chalk.blue(`[${clientId}]`) : chalk.blue('[ALL]')
const color = source === 'server' ? pc.magenta : pc.green
const sourceLabel = source === 'server' ? pc.gray(' (server-generated)') : ''
const clientLabel = clientId ? pc.blue(`[${clientId}]`) : pc.blue('[ALL]')
logger?.log(color('→ Playwright'), clientLabel + ':', method + detailsStr + sourceLabel)
}
}
@@ -246,7 +246,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
try {
logger?.log(chalk.blue('Auto-creating initial tab for Playwright client'))
logger?.log(pc.blue('Auto-creating initial tab for Playwright client'))
const result = await sendToExtension({ method: 'createInitialTab', timeout: 10000 }) as {
success: boolean
tabId: number
@@ -259,7 +259,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
targetId: result.targetInfo.targetId,
targetInfo: result.targetInfo
})
logger?.log(chalk.blue(`Auto-created tab, now have ${connectedTargets.size} targets, url: ${result.targetInfo.url}`))
logger?.log(pc.blue(`Auto-created tab, now have ${connectedTargets.size} targets, url: ${result.targetInfo.url}`))
}
} catch (e) {
logger?.error('Failed to auto-create initial tab:', e)
@@ -377,7 +377,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
const timeout = setTimeout(() => {
emitter.off('cdp:event', handler)
logger?.log(chalk.yellow(`IMPORTANT: Runtime.enable timed out waiting for main frame executionContextCreated (sessionId: ${sessionId}). This may cause pages to not be visible immediately.`))
logger?.log(pc.yellow(`IMPORTANT: Runtime.enable timed out waiting for main frame executionContextCreated (sessionId: ${sessionId}). This may cause pages to not be visible immediately.`))
resolve()
}, 3000)
emitter.on('cdp:event', handler)
@@ -500,7 +500,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
try {
const { level, args } = await c.req.json()
const logFn = (logger as any)?.[level] || logger?.log
const prefix = chalk.red(`[MCP] [${level.toUpperCase()}]`)
const prefix = pc.red(`[MCP] [${level.toUpperCase()}]`)
logFn?.(prefix, ...args)
return c.json({ ok: true })
} catch {
@@ -520,11 +520,11 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
if (origin.startsWith('chrome-extension://')) {
const extensionId = origin.replace('chrome-extension://', '')
if (!OUR_EXTENSION_IDS.includes(extensionId)) {
logger?.log(chalk.red(`Rejecting /cdp WebSocket from unknown extension: ${extensionId}`))
logger?.log(pc.red(`Rejecting /cdp WebSocket from unknown extension: ${extensionId}`))
return c.text('Forbidden', 403)
}
} else {
logger?.log(chalk.red(`Rejecting /cdp WebSocket from origin: ${origin}`))
logger?.log(pc.red(`Rejecting /cdp WebSocket from origin: ${origin}`))
return c.text('Forbidden', 403)
}
}
@@ -543,14 +543,14 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
return {
async onOpen(_event, ws) {
if (playwrightClients.has(clientId)) {
logger?.log(chalk.red(`Rejecting duplicate client ID: ${clientId}`))
logger?.log(pc.red(`Rejecting duplicate client ID: ${clientId}`))
ws.close(1000, 'Client ID already connected')
return
}
// Add client first so it can receive Target.attachedToTarget events
playwrightClients.set(clientId, { id: clientId, ws })
logger?.log(chalk.green(`Playwright client connected: ${clientId} (${playwrightClients.size} total) (extension? ${!!extensionWs}) (${connectedTargets.size} pages)`))
logger?.log(pc.green(`Playwright client connected: ${clientId} (${playwrightClients.size} total) (extension? ${!!extensionWs}) (${connectedTargets.size} pages)`))
},
async onMessage(event, ws) {
@@ -607,9 +607,9 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
} satisfies CDPEventFor<'Target.attachedToTarget'>
if (!target.targetInfo.url) {
logger?.error(chalk.red('[Server] WARNING: Target.attachedToTarget sent with empty URL!'), JSON.stringify(attachedPayload))
logger?.error(pc.red('[Server] WARNING: Target.attachedToTarget sent with empty URL!'), JSON.stringify(attachedPayload))
}
logger?.log(chalk.magenta('[Server] Target.attachedToTarget full payload:'), JSON.stringify(attachedPayload))
logger?.log(pc.magenta('[Server] Target.attachedToTarget full payload:'), JSON.stringify(attachedPayload))
sendToPlaywright({
message: attachedPayload,
clientId,
@@ -634,9 +634,9 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
} satisfies CDPEventFor<'Target.targetCreated'>
if (!target.targetInfo.url) {
logger?.error(chalk.red('[Server] WARNING: Target.targetCreated sent with empty URL!'), JSON.stringify(targetCreatedPayload))
logger?.error(pc.red('[Server] WARNING: Target.targetCreated sent with empty URL!'), JSON.stringify(targetCreatedPayload))
}
logger?.log(chalk.magenta('[Server] Target.targetCreated full payload:'), JSON.stringify(targetCreatedPayload))
logger?.log(pc.magenta('[Server] Target.targetCreated full payload:'), JSON.stringify(targetCreatedPayload))
sendToPlaywright({
message: targetCreatedPayload,
clientId,
@@ -661,9 +661,9 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
}
} satisfies CDPEventFor<'Target.attachedToTarget'>
if (!target.targetInfo.url) {
logger?.error(chalk.red('[Server] WARNING: Target.attachedToTarget (from attachToTarget) sent with empty URL!'), JSON.stringify(attachedPayload))
logger?.error(pc.red('[Server] WARNING: Target.attachedToTarget (from attachToTarget) sent with empty URL!'), JSON.stringify(attachedPayload))
}
logger?.log(chalk.magenta('[Server] Target.attachedToTarget (from attachToTarget) payload:'), JSON.stringify(attachedPayload))
logger?.log(pc.magenta('[Server] Target.attachedToTarget (from attachToTarget) payload:'), JSON.stringify(attachedPayload))
sendToPlaywright({
message: attachedPayload,
clientId,
@@ -689,7 +689,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
onClose() {
playwrightClients.delete(clientId)
logger?.log(chalk.yellow(`Playwright client disconnected: ${clientId} (${playwrightClients.size} remaining)`))
logger?.log(pc.yellow(`Playwright client disconnected: ${clientId} (${playwrightClients.size} remaining)`))
},
onError(event) {
@@ -707,7 +707,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
const isLocalhost = remoteAddress === '127.0.0.1' || remoteAddress === '::1'
if (!isLocalhost) {
logger?.log(chalk.red(`Rejecting /extension WebSocket from remote IP: ${remoteAddress}`))
logger?.log(pc.red(`Rejecting /extension WebSocket from remote IP: ${remoteAddress}`))
return c.text('Forbidden - Extension must be local', 403)
}
@@ -716,13 +716,13 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
// is coming from our specific Chrome Extension, not a malicious website.
const origin = c.req.header('origin')
if (!origin || !origin.startsWith('chrome-extension://')) {
logger?.log(chalk.red(`Rejecting /extension WebSocket: origin must be chrome-extension://, got: ${origin || 'none'}`))
logger?.log(pc.red(`Rejecting /extension WebSocket: origin must be chrome-extension://, got: ${origin || 'none'}`))
return c.text('Forbidden', 403)
}
const extensionId = origin.replace('chrome-extension://', '')
if (!OUR_EXTENSION_IDS.includes(extensionId)) {
logger?.log(chalk.red(`Rejecting /extension WebSocket from unknown extension: ${extensionId}`))
logger?.log(pc.red(`Rejecting /extension WebSocket from unknown extension: ${extensionId}`))
return c.text('Forbidden', 403)
}
@@ -731,7 +731,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
return {
onOpen(_event, ws) {
if (extensionWs) {
logger?.log(chalk.yellow('Closing existing extension connection to replace with new one'))
logger?.log(pc.yellow('Closing existing extension connection to replace with new one'))
extensionWs.close(4001, 'Extension Replaced')
// Clear state from the old connection to prevent leaks
@@ -781,7 +781,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
} else if (message.method === 'log') {
const { level, args } = message.params
const logFn = (logger as any)?.[level] || logger?.log
const prefix = chalk.yellow(`[Extension] [${level.toUpperCase()}]`)
const prefix = pc.yellow(`[Extension] [${level.toUpperCase()}]`)
logFn?.(prefix, ...args)
} else {
const extensionEvent = message as ExtensionEventMessage
@@ -809,14 +809,14 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
// These targets can't be properly controlled through chrome.debugger API
// and cause issues when Playwright tries to initialize them (issue #14)
if (isRestrictedTarget(targetParams.targetInfo)) {
logger?.log(chalk.gray(`[Server] Ignoring restricted target: ${targetParams.targetInfo.type} (${targetParams.targetInfo.url})`))
logger?.log(pc.gray(`[Server] Ignoring restricted target: ${targetParams.targetInfo.type} (${targetParams.targetInfo.url})`))
return
}
if (!targetParams.targetInfo.url) {
logger?.error(chalk.red('[Extension] WARNING: Target.attachedToTarget received with empty URL!'), JSON.stringify({ method, params: targetParams, sessionId }))
logger?.error(pc.red('[Extension] WARNING: Target.attachedToTarget received with empty URL!'), JSON.stringify({ method, params: targetParams, sessionId }))
}
logger?.log(chalk.yellow('[Extension] Target.attachedToTarget full payload:'), JSON.stringify({ method, params: targetParams, sessionId }))
logger?.log(pc.yellow('[Extension] Target.attachedToTarget full payload:'), JSON.stringify({ method, params: targetParams, sessionId }))
// Check if we already sent this target to clients (e.g., from Target.setAutoAttach response)
const alreadyConnected = connectedTargets.has(targetParams.sessionId)
@@ -854,7 +854,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
for (const [sid, target] of connectedTargets.entries()) {
if (target.targetId === crashParams.targetId) {
connectedTargets.delete(sid)
logger?.log(chalk.red('[Server] Target crashed, removing:'), crashParams.targetId)
logger?.log(pc.red('[Server] Target crashed, removing:'), crashParams.targetId)
break
}
}
@@ -892,7 +892,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
url: frameParams.frame.url,
title: frameParams.frame.name || target.targetInfo.title,
}
logger?.log(chalk.magenta('[Server] Updated target URL from Page.frameNavigated:'), frameParams.frame.url)
logger?.log(pc.magenta('[Server] Updated target URL from Page.frameNavigated:'), frameParams.frame.url)
}
}
@@ -913,7 +913,7 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
...target.targetInfo,
url: navParams.url,
}
logger?.log(chalk.magenta('[Server] Updated target URL from Page.navigatedWithinDocument:'), navParams.url)
logger?.log(pc.magenta('[Server] Updated target URL from Page.navigatedWithinDocument:'), navParams.url)
}
}
+269 -253
View File
File diff suppressed because it is too large Load Diff