restart ws process if already running with different version

This commit is contained in:
Tommy D. Rossi
2025-12-21 16:48:07 +01:00
parent ba8096fd74
commit 6809ebea72
9 changed files with 239 additions and 117 deletions
+2
View File
@@ -328,6 +328,7 @@ function onDebuggerDetach(source: chrome.debugger.Debuggee, reason: `${chrome.de
})
if (reason === chrome.debugger.DetachReason.CANCELED_BY_USER) {
// Chrome's debugger info bar cancellation detaches ALL debugger sessions, not just one tab
store.setState({ connectionState: 'disconnected', errorText: undefined })
}
}
@@ -451,6 +452,7 @@ function handleConnectionClose(reason: string, code: number): void {
if (reason === 'Extension Replaced' || code === 4001) {
logger.debug('Connection replaced by another extension instance')
store.setState({
tabs: new Map(),
connectionState: 'error',
errorText: 'Disconnected: Replaced by another extension',
})
+1
View File
@@ -45,6 +45,7 @@
"devtools-protocol": "^0.0.1543509",
"diff": "^8.0.2",
"hono": "^4.10.6",
"kill-port-process": "^3.2.1",
"playwright-core": "^1.56.1",
"string-dedent": "^3.0.2",
"user-agents": "^1.1.669",
-21
View File
@@ -9,33 +9,12 @@ export type Logger = {
logFilePath: string
}
function cleanupOldLogs(logsDir: string): void {
if (!fs.existsSync(logsDir)) {
return
}
const files = fs.readdirSync(logsDir)
.filter((f) => f.startsWith('relay-server-') && f.endsWith('.log'))
.map((f) => ({
name: f,
path: path.join(logsDir, f),
mtime: fs.statSync(path.join(logsDir, f)).mtime.getTime(),
}))
.sort((a, b) => b.mtime - a.mtime)
if (files.length > 10) {
files.slice(10).forEach((f) => {
fs.unlinkSync(f.path)
})
}
}
export function createFileLogger({ logFilePath }: { logFilePath?: string } = {}): Logger {
const resolvedLogFilePath = logFilePath || LOG_FILE_PATH
const logDir = path.dirname(resolvedLogFilePath)
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true })
}
cleanupOldLogs(logDir)
fs.writeFileSync(resolvedLogFilePath, '')
let queue: Promise<void> = Promise.resolve()
+5
View File
@@ -7,6 +7,7 @@ import type { CDPCommand, CDPResponseBase, CDPEventBase, CDPEventFor, RelayServe
import type { ExtensionMessage, ExtensionEventMessage } from './protocol.js'
import chalk from 'chalk'
import { EventEmitter } from 'node:events'
import { VERSION } from '../utils.js'
type ConnectedTarget = {
sessionId: string
@@ -276,6 +277,10 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
return c.text('OK')
})
app.get('/version', (c) => {
return c.json({ version: VERSION })
})
app.post('/mcp-log', async (c) => {
try {
const { level, args } = await c.req.json()
+14 -12
View File
@@ -14,6 +14,7 @@ import { getCDPSessionForPage } from './cdp-session.js'
import { startPlayWriterCDPRelayServer, type RelayServer } from './extension/cdp-relay.js'
import { createFileLogger } from './create-logger.js'
import type { CDPCommand } from './cdp-types.js'
import { killPortProcess } from 'kill-port-process'
declare const window: any
declare const document: any
@@ -51,15 +52,10 @@ function js(strings: TemplateStringsArray, ...values: any[]): string {
async function killProcessOnPort(port: number): Promise<void> {
try {
const { stdout } = await execAsync(`lsof -ti:${port}`)
const pids = stdout.trim().split('\n').filter(Boolean)
if (pids.length > 0) {
await execAsync(`kill -9 ${pids.join(' ')}`)
console.log(`Killed processes ${pids.join(', ')} on port ${port}`)
await new Promise((resolve) => setTimeout(resolve, 1000))
}
} catch (error) {
// No process running on port or already killed
await killPortProcess(port)
console.log(`Killed processes on port ${port}`)
} catch (err) {
console.error('Error killing process on port:', err)
}
}
@@ -1635,7 +1631,7 @@ describe('MCP Server Tests', () => {
const wsUrl = getCdpUrl()
const client = await getCDPSessionForPage({ page: cdpPage!, wsUrl })
const layoutMetrics = await client.send('Page.getLayoutMetrics')
expect(layoutMetrics.cssVisualViewport).toBeDefined()
expect(layoutMetrics.cssVisualViewport.clientWidth).toBeGreaterThan(0)
@@ -1968,14 +1964,20 @@ describe('CDP Session Tests', () => {
sampleFunctionNames: functionNames,
}).toMatchInlineSnapshot(`
{
"durationMicroseconds": 15772,
"durationMicroseconds": 7398,
"hasNodes": true,
"nodeCount": 7,
"nodeCount": 21,
"sampleFunctionNames": [
"(root)",
"(program)",
"(idle)",
"evaluate",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
],
}
`)
+32 -17
View File
@@ -11,7 +11,8 @@ import { fileURLToPath } from 'node:url'
import vm from 'node:vm'
import dedent from 'string-dedent'
import { createPatch } from 'diff'
import { getCdpUrl, LOG_FILE_PATH } from './utils.js'
import { getCdpUrl, LOG_FILE_PATH, VERSION } from './utils.js'
import { killPortProcess } from 'kill-port-process'
import { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
import { getCDPSessionForPage, CDPSession } from './cdp-session.js'
@@ -152,7 +153,7 @@ function clearConnectionState() {
async function sendLogToRelayServer(level: string, ...args: any[]) {
try {
await fetch(`http://localhost:${RELAY_PORT}/mcp-log`, {
await fetch(`http://127.0.0.1:${RELAY_PORT}/mcp-log`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ level, args }),
@@ -163,25 +164,41 @@ async function sendLogToRelayServer(level: string, ...args: any[]) {
}
}
async function isPortTaken(port: number): Promise<boolean> {
async function getServerVersion(port: number): Promise<string | null> {
try {
const response = await fetch(`http://localhost:${port}/`, {
signal: AbortSignal.timeout(100),
const response = await fetch(`http://127.0.0.1:${port}/version`, {
signal: AbortSignal.timeout(500),
})
return response.ok
} catch {
return false
if (!response.ok) {
return null
}
const data = (await response.json()) as { version: string }
return data.version
} catch (error) {
return null
}
}
async function ensureRelayServer(): Promise<void> {
const portTaken = await isPortTaken(RELAY_PORT)
async function killRelayServer(port: number): Promise<void> {
try {
await killPortProcess(port)
await new Promise((resolve) => setTimeout(resolve, 500))
} catch {}
}
if (portTaken) {
async function ensureRelayServer(): Promise<void> {
const serverVersion = await getServerVersion(RELAY_PORT)
if (serverVersion === VERSION) {
return
}
console.error('CDP relay server not running, starting it...')
if (serverVersion !== null) {
console.error(`CDP relay server version mismatch (server: ${serverVersion}, mcp: ${VERSION}), restarting...`)
await killRelayServer(RELAY_PORT)
} else {
console.error('CDP relay server not running, starting it...')
}
const scriptPath = require.resolve('../dist/start-relay-server.js')
@@ -194,8 +211,8 @@ async function ensureRelayServer(): Promise<void> {
for (let i = 0; i < 10; i++) {
await new Promise((resolve) => setTimeout(resolve, 500))
const isRunning = await isPortTaken(RELAY_PORT)
if (isRunning) {
const newVersion = await getServerVersion(RELAY_PORT)
if (newVersion === VERSION) {
console.error('CDP relay server started successfully')
return
}
@@ -622,9 +639,7 @@ server.tool(
timeout,
displayErrors: true,
}),
new Promise((_, reject) =>
setTimeout(() => reject(new CodeExecutionTimeoutError(timeout)), timeout),
),
new Promise((_, reject) => setTimeout(() => reject(new CodeExecutionTimeoutError(timeout)), timeout)),
])
let responseText = formatConsoleLogs(consoleLogs)
@@ -69,76 +69,76 @@ Return value:
- generic [ref=e50]: Theme
- combobox "Theme" [ref=e51]:
- generic [ref=e52]: "Theme:"
- generic: Neutral
- img
- button "Copy Code" [ref=e53]:
- combobox [ref=e53]
- button "Copy Code" [ref=e54]:
- img
- generic [ref=e54]: Copy Code
- generic [ref=e58]:
- generic [ref=e62]:
- group "Payment Method" [ref=e63]:
- generic [ref=e64]: Payment Method
- paragraph [ref=e65]: All transactions are secure and encrypted
- generic [ref=e66]:
- group [ref=e67]:
- generic [ref=e68]: Name on Card
- textbox "Name on Card" [ref=e69]:
- generic [ref=e55]: Copy Code
- generic [ref=e59]:
- generic [ref=e63]:
- group "Payment Method" [ref=e64]:
- generic [ref=e65]: Payment Method
- paragraph [ref=e66]: All transactions are secure and encrypted
- generic [ref=e67]:
- group [ref=e68]:
- generic [ref=e69]: Name on Card
- textbox "Name on Card" [ref=e70]:
- /placeholder: John Doe
- generic [ref=e70]:
- group [ref=e71]:
- generic [ref=e72]: Card Number
- textbox "Card Number" [ref=e73]:
- generic [ref=e71]:
- group [ref=e72]:
- generic [ref=e73]: Card Number
- textbox "Card Number" [ref=e74]:
- /placeholder: 1234 5678 9012 3456
- paragraph [ref=e74]: Enter your 16-digit number.
- group [ref=e75]:
- generic [ref=e76]: CVV
- textbox "CVV" [ref=e77]:
- paragraph [ref=e75]: Enter your 16-digit number.
- group [ref=e76]:
- generic [ref=e77]: CVV
- textbox "CVV" [ref=e78]:
- /placeholder: "123"
- generic [ref=e78]:
- group [ref=e79]:
- generic [ref=e80]: Month
- combobox "Month" [ref=e81]:
- generic [ref=e79]:
- group [ref=e80]:
- generic [ref=e81]: Month
- combobox "Month" [ref=e82]:
- generic: MM
- img
- combobox [ref=e82]
- group [ref=e83]:
- generic [ref=e84]: Year
- combobox "Year" [ref=e85]:
- combobox [ref=e83]
- group [ref=e84]:
- generic [ref=e85]: Year
- combobox "Year" [ref=e86]:
- generic: YYYY
- img
- combobox [ref=e86]
- group "Billing Address" [ref=e88]:
- generic [ref=e89]: Billing Address
- paragraph [ref=e90]: The billing address associated with your payment method
- group [ref=e92]:
- checkbox "Same as shipping address" [checked] [ref=e93]:
- combobox [ref=e87]
- group "Billing Address" [ref=e89]:
- generic [ref=e90]: Billing Address
- paragraph [ref=e91]: The billing address associated with your payment method
- group [ref=e93]:
- checkbox "Same as shipping address" [checked] [ref=e94]:
- generic:
- img
- checkbox [checked]
- generic [ref=e94]: Same as shipping address
- group [ref=e96]:
- group [ref=e98]:
- generic [ref=e99]: Comments
- textbox "Comments" [ref=e100]:
- generic [ref=e95]: Same as shipping address
- group [ref=e97]:
- group [ref=e99]:
- generic [ref=e100]: Comments
- textbox "Comments" [ref=e101]:
- /placeholder: Add any additional comments
- group [ref=e101]:
- button "Submit" [ref=e102]
- button "Cancel" [ref=e103]
- generic [ref=e104]:
- generic [ref=e105]:
- generic [ref=e106]:
- generic [ref=e108]:
- img "@shadcn" [ref=e110]
- img "@maxleiter" [ref=e112]
- img "@evilrabbit" [ref=e114]
- generic [ref=e115]: No Team Members
- generic [ref=e116]: Invite your team to collaborate on this project.
- button "Invite Members" [ref=e118]:
- group [ref=e102]:
- button "Submit" [ref=e103]
- button "Cancel" [ref=e104]
- generic [ref=e105]:
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e109]:
- generic [ref=e111]: CN
- generic [ref=e113]: LR
- generic [ref=e115]: ER
- generic [ref=e116]: No Team Members
- generic [ref=e117]: Invite your team to collaborate on this project.
- button "Invite Members" [ref=e119]:
- img
- text: Invite Members
- generic [ref=e119]:
- generic [ref=e120]:
- generic [ref=e120]:
- generic [ref=e121]:
- status "Loading"
- tex
[Truncated to 6000 characters. Better manage your logs or paginate them to read the full logs]
+4 -12
View File
@@ -1,22 +1,14 @@
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
export function getCdpUrl({ port = 19988, host = '127.0.0.1' }: { port?: number; host?: string } = {}) {
const id = `${Math.random().toString(36).substring(2, 15)}_${Date.now()}`
return `ws://${host}:${port}/cdp/${id}`
}
function getLogFilePath(): string {
if (process.env.PLAYWRITER_LOG_PATH) {
return process.env.PLAYWRITER_LOG_PATH
}
const logsDir = path.join(os.tmpdir(), 'playwriter')
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true })
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
return path.join(logsDir, `relay-server-${timestamp}.log`)
}
export const LOG_FILE_PATH = path.join(os.tmpdir(), 'playwriter', 'relay-server.log')
export const LOG_FILE_PATH = getLogFilePath()
const packageJsonPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json')
export const VERSION = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')).version as string
+126
View File
@@ -81,6 +81,9 @@ importers:
hono:
specifier: ^4.10.6
version: 4.10.6
kill-port-process:
specifier: ^3.2.1
version: 3.2.1
playwright-core:
specifier: ^1.56.1
version: 1.56.1
@@ -1204,6 +1207,9 @@ packages:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
cross-spawn@5.1.0:
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -1409,6 +1415,10 @@ packages:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
execa@0.9.0:
resolution: {integrity: sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==}
engines: {node: '>=4'}
expect-type@1.2.2:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
@@ -1561,10 +1571,17 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stream@3.0.0:
resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
engines: {node: '>=4'}
get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
get-them-args@1.3.2:
resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==}
get-tsconfig@4.13.0:
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
@@ -1711,6 +1728,10 @@ packages:
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
is-stream@1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
is-subdir@1.2.0:
resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
engines: {node: '>=4'}
@@ -1767,6 +1788,11 @@ packages:
jws@4.0.1:
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
kill-port-process@3.2.1:
resolution: {integrity: sha512-9rRL5uivhIaW82ES0xTeciG5dfHUVQ0MqUycyf6Y1GBzDNB/fHxKStFUqnL4hCwGp89AcrKIjJWEVZHhSUi9LQ==}
engines: {node: '>=14'}
hasBin: true
langsmith@0.3.82:
resolution: {integrity: sha512-RTcxtRm0zp2lV+pMesMW7EZSsIlqN7OmR2F6sZ/sOFQwmcLVl+VErMPV4VkX4Sycs4/EIAFT5hpr36EqiHoikQ==}
peerDependencies:
@@ -1807,6 +1833,9 @@ packages:
resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
engines: {node: 20 || >=22}
lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
@@ -1924,6 +1953,10 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
npm-run-path@2.0.2:
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
engines: {node: '>=4'}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2034,6 +2067,10 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
path-key@2.0.1:
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
engines: {node: '>=4'}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -2066,6 +2103,10 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pid-from-port@1.1.3:
resolution: {integrity: sha512-OlE82n3yMOE5dY9RMOwxhoWefeMlxwk5IVxoj0sSzSFIlmvhN4obzTvO3s/d/b5JhcgXikjaspsy/HuUDTqbBg==}
engines: {node: '>=4'}
pify@4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
engines: {node: '>=6'}
@@ -2138,6 +2179,9 @@ packages:
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
pseudomap@1.0.2:
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
pump@3.0.3:
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
@@ -2260,10 +2304,18 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
shebang-regex@1.0.0:
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
engines: {node: '>=0.10.0'}
shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
@@ -2291,6 +2343,9 @@ packages:
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@@ -2383,6 +2438,10 @@ packages:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
strip-eof@1.0.0:
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
engines: {node: '>=0.10.0'}
strip-json-comments@5.0.3:
resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
engines: {node: '>=14.16'}
@@ -2674,6 +2733,10 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
which@1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -2718,6 +2781,9 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yallist@2.1.2:
resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -3892,6 +3958,12 @@ snapshots:
object-assign: 4.1.1
vary: 1.1.2
cross-spawn@5.1.0:
dependencies:
lru-cache: 4.1.5
shebang-command: 1.2.0
which: 1.3.1
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -4118,6 +4190,16 @@ snapshots:
dependencies:
eventsource-parser: 3.0.6
execa@0.9.0:
dependencies:
cross-spawn: 5.1.0
get-stream: 3.0.0
is-stream: 1.1.0
npm-run-path: 2.0.2
p-finally: 1.0.0
signal-exit: 3.0.7
strip-eof: 1.0.0
expect-type@1.2.2: {}
express-rate-limit@7.5.1(express@5.1.0):
@@ -4322,11 +4404,15 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stream@3.0.0: {}
get-stream@5.2.0:
dependencies:
pump: 3.0.3
optional: true
get-them-args@1.3.2: {}
get-tsconfig@4.13.0:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -4481,6 +4567,8 @@ snapshots:
is-promise@4.0.0: {}
is-stream@1.1.0: {}
is-subdir@1.2.0:
dependencies:
better-path-resolve: 1.0.0
@@ -4562,6 +4650,11 @@ snapshots:
jwa: 2.0.1
safe-buffer: 5.2.1
kill-port-process@3.2.1:
dependencies:
get-them-args: 1.3.2
pid-from-port: 1.1.3
langsmith@0.3.82(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76)):
dependencies:
'@types/uuid': 10.0.0
@@ -4597,6 +4690,11 @@ snapshots:
lru-cache@11.2.2:
optional: true
lru-cache@4.1.5:
dependencies:
pseudomap: 1.0.2
yallist: 2.1.2
lru-cache@7.18.3:
optional: true
@@ -4678,6 +4776,10 @@ snapshots:
normalize-path@3.0.0: {}
npm-run-path@2.0.2:
dependencies:
path-key: 2.0.1
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
@@ -4788,6 +4890,8 @@ snapshots:
path-exists@4.0.0: {}
path-key@2.0.1: {}
path-key@3.1.1: {}
path-scurry@1.11.1:
@@ -4810,6 +4914,10 @@ snapshots:
picomatch@4.0.3: {}
pid-from-port@1.1.3:
dependencies:
execa: 0.9.0
pify@4.0.1: {}
pino-abstract-transport@2.0.0:
@@ -4901,6 +5009,8 @@ snapshots:
proxy-from-env@1.1.0:
optional: true
pseudomap@1.0.2: {}
pump@3.0.3:
dependencies:
end-of-stream: 1.4.5
@@ -5063,10 +5173,16 @@ snapshots:
setprototypeof@1.2.0: {}
shebang-command@1.2.0:
dependencies:
shebang-regex: 1.0.0
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@1.0.0: {}
shebang-regex@3.0.0: {}
shell-quote@1.8.3: {}
@@ -5101,6 +5217,8 @@ snapshots:
siginfo@2.0.0: {}
signal-exit@3.0.7: {}
signal-exit@4.1.0: {}
simple-wcswidth@1.1.2: {}
@@ -5193,6 +5311,8 @@ snapshots:
strip-bom@3.0.0: {}
strip-eof@1.0.0: {}
strip-json-comments@5.0.3: {}
supports-color@7.2.0:
@@ -5462,6 +5582,10 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
which@1.3.1:
dependencies:
isexe: 2.0.0
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -5497,6 +5621,8 @@ snapshots:
y18n@5.0.8: {}
yallist@2.1.2: {}
yargs-parser@21.1.1: {}
yargs@17.7.2: