browsertrack 0.2.0 → 0.2.2

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.
Files changed (67) hide show
  1. package/AGENTS.md +11 -6
  2. package/README.md +2 -0
  3. package/dist/{chunk-WB7ZKWK7.js → chunk-4HRLW6YF.js} +509 -109
  4. package/dist/chunk-4HRLW6YF.js.map +1 -0
  5. package/dist/{chunk-3HOXPTM2.js → chunk-AYSVE6NG.js} +808 -53
  6. package/dist/chunk-AYSVE6NG.js.map +1 -0
  7. package/dist/{chunk-6VA7GBAO.js → chunk-QRZ57ME3.js} +70 -2
  8. package/dist/chunk-QRZ57ME3.js.map +1 -0
  9. package/dist/{chunk-UP5JKCFY.js → chunk-TWEYRBDU.js} +281 -44
  10. package/dist/chunk-TWEYRBDU.js.map +1 -0
  11. package/dist/cli/index.js +1040 -546
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/client/index.cjs +536 -109
  14. package/dist/client/index.d.ts +36 -4
  15. package/dist/client/index.js +8 -4
  16. package/dist/client.iife.js +42 -19
  17. package/dist/{notes-BMnonq46.d.ts → commands-fjuqKzkm.d.ts} +125 -114
  18. package/dist/core/index.d.ts +26 -3
  19. package/dist/core/index.js +9 -1
  20. package/dist/daemon/index.d.ts +4 -4
  21. package/dist/daemon/index.js +6 -8
  22. package/dist/{engine-B43IohQY.d.ts → engine-CmchnMDq.d.ts} +2 -2
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.js +14 -7
  25. package/dist/mcp/index.d.ts +4 -4
  26. package/dist/mcp/index.js +7 -4
  27. package/dist/{projects-D5J-egVN.d.ts → projects-DB7S312i.d.ts} +1 -1
  28. package/dist/{server-BztYp1Zc.d.ts → server-DjV7RWQM.d.ts} +10 -2
  29. package/docs/cli.md +4 -1
  30. package/docs/component-resolver.md +108 -0
  31. package/docs/getting-started.md +60 -6
  32. package/docs/index.md +1 -0
  33. package/docs/mcp-reference.md +44 -2
  34. package/docs/visual-notes.md +36 -0
  35. package/package.json +1 -1
  36. package/packages/cli/src/index.ts +247 -151
  37. package/packages/client/src/client.ts +18 -1
  38. package/packages/client/src/config.ts +84 -1
  39. package/packages/client/src/index.ts +4 -1
  40. package/packages/client/src/interceptors/interaction.ts +3 -0
  41. package/packages/client/src/interceptors/navigation.ts +38 -26
  42. package/packages/client/src/interceptors/network.ts +22 -17
  43. package/packages/client/src/notes/inspector.ts +186 -51
  44. package/packages/client/src/source/resolver.ts +278 -0
  45. package/packages/client/src/transport/websocket.ts +23 -18
  46. package/packages/core/src/index.ts +1 -0
  47. package/packages/core/src/safety.ts +86 -0
  48. package/packages/core/src/types/events.ts +3 -0
  49. package/packages/core/src/types/notes.ts +11 -0
  50. package/packages/daemon/src/server/daemon.ts +7 -1
  51. package/packages/daemon/src/server/http.ts +125 -5
  52. package/packages/daemon/src/server/ws.ts +33 -29
  53. package/packages/daemon/src/storage/db.ts +57 -35
  54. package/packages/mcp/src/handlers.ts +115 -45
  55. package/packages/mcp/src/server.ts +202 -2
  56. package/test/client/component-resolver.test.ts +141 -0
  57. package/test/client/interceptors.test.ts +56 -0
  58. package/test/core/safety.test.ts +106 -0
  59. package/test/daemon/storage.test.ts +36 -0
  60. package/test/e2e/daemon-mcp-e2e.test.ts +10 -0
  61. package/test/mcp/auto-start.test.ts +87 -0
  62. package/dist/chunk-3HOXPTM2.js.map +0 -1
  63. package/dist/chunk-6VA7GBAO.js.map +0 -1
  64. package/dist/chunk-7OCOQGDN.js +0 -635
  65. package/dist/chunk-7OCOQGDN.js.map +0 -1
  66. package/dist/chunk-UP5JKCFY.js.map +0 -1
  67. package/dist/chunk-WB7ZKWK7.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/daemon/src/incidents/engine.ts","../packages/daemon/src/server/http.ts","../packages/daemon/src/server/ws.ts","../packages/daemon/src/server/daemon.ts","../packages/daemon/src/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport type {\n ClientEventMessage,\n ConsoleEvent,\n Incident,\n IncidentOccurrence,\n IncidentSeverity,\n NetworkEvent,\n RuntimeErrorEvent,\n} from '../../../core/src/index.js';\nimport { computeFingerprint, extractSourceFromStack, normalizeSourceFile, redactSensitiveData } from '../../../core/src/index.js';\nimport type { StorageDB } from '../storage/db.js';\nimport type { ScreenshotStore } from '../storage/screenshot-store.js';\n\nexport class IncidentEngine {\n private db: StorageDB;\n private screenshotStore: ScreenshotStore;\n\n constructor(db: StorageDB, screenshotStore: ScreenshotStore) {\n this.db = db;\n this.screenshotStore = screenshotStore;\n }\n\n public processClientEvent(message: ClientEventMessage & { screenshot?: string }): Incident | null {\n const session = this.db.getSession(message.sessionId);\n const projectId = session?.projectId || 'default';\n const sanitizedBreadcrumbs = (message.breadcrumbs || []).map((b) => redactSensitiveData(b));\n const sanitizedLastElement = message.lastElement ? redactSensitiveData(message.lastElement) : undefined;\n\n // Filter network failures from breadcrumbs\n const networkFailures: NetworkEvent[] = sanitizedBreadcrumbs\n .filter((b) => (b.type === 'fetch' || b.type === 'xhr') && b.level === 'error' && b.data)\n .map((b) => ({\n url: b.message.split(' ')[1] || '',\n method: b.message.split(' ')[0] || 'GET',\n status: b.data?.status,\n durationMs: b.data?.durationMs || 0,\n error: b.data?.error,\n aborted: b.data?.aborted,\n timestamp: b.timestamp,\n }));\n\n if (message.eventType === 'runtime_error' || message.eventType === 'unhandled_rejection') {\n const payload = message.payload as RuntimeErrorEvent;\n return this.handleErrorEvent({\n projectId,\n sessionId: message.sessionId,\n type: payload.errorType || 'runtime_exception',\n severity: 'error',\n message: payload.message || 'Unknown runtime error',\n stack: payload.stack,\n sourceFile: payload.filename,\n line: payload.lineno,\n column: payload.colno,\n route: message.route || '/',\n url: message.url,\n breadcrumbs: sanitizedBreadcrumbs,\n networkFailures,\n lastElement: sanitizedLastElement,\n screenshotDataUrl: message.screenshot,\n });\n }\n\n if (message.eventType === 'console') {\n const payload = message.payload as ConsoleEvent;\n if (payload.level === 'error') {\n return this.handleErrorEvent({\n projectId,\n sessionId: message.sessionId,\n type: 'console_error',\n severity: 'error',\n message: payload.message,\n stack: payload.stack,\n route: message.route || '/',\n url: message.url,\n breadcrumbs: sanitizedBreadcrumbs,\n networkFailures,\n lastElement: sanitizedLastElement,\n screenshotDataUrl: message.screenshot,\n });\n }\n }\n\n return null;\n }\n\n private handleErrorEvent(input: {\n projectId: string;\n sessionId: string;\n type: string;\n severity: IncidentSeverity;\n message: string;\n stack?: string;\n sourceFile?: string;\n line?: number;\n column?: number;\n route: string;\n url: string;\n breadcrumbs: any[];\n networkFailures: NetworkEvent[];\n lastElement?: any;\n screenshotDataUrl?: string;\n }): Incident {\n const extracted = (!input.sourceFile || !input.line) && input.stack ? extractSourceFromStack(input.stack) : null;\n\n const rawSource = input.sourceFile || extracted?.file || 'unknown_source';\n const sourceFile = normalizeSourceFile(rawSource);\n const line = input.line || extracted?.line || 0;\n const column = input.column || extracted?.column;\n\n const fingerprint = computeFingerprint({\n type: input.type,\n message: input.message,\n sourceFile,\n line,\n column,\n stack: input.stack,\n });\n\n const now = new Date().toISOString();\n const existing = this.db.findIncidentByFingerprint(fingerprint);\n\n if (existing) {\n const updatedOccurrences = existing.occurrences + 1;\n this.db.updateIncidentOccurrence(existing.id, {\n sessionId: input.sessionId,\n lastSeen: now,\n occurrences: updatedOccurrences,\n route: input.route,\n breadcrumbs: input.breadcrumbs,\n lastElement: input.lastElement,\n stack: input.stack,\n });\n\n // Insert occurrence\n const occurrenceId = `occ_${crypto.randomUUID().slice(0, 8)}`;\n this.db.insertIncidentOccurrence({\n id: occurrenceId,\n incidentId: existing.id,\n sessionId: input.sessionId,\n timestamp: now,\n route: input.route,\n url: input.url,\n stack: input.stack,\n breadcrumbs: input.breadcrumbs,\n lastElement: input.lastElement,\n });\n\n return {\n ...existing,\n occurrences: updatedOccurrences,\n lastSeen: now,\n breadcrumbs: input.breadcrumbs,\n lastElement: input.lastElement || existing.lastElement,\n };\n }\n\n // New incident\n const incidentId = `inc_${crypto.randomUUID().slice(0, 8)}`;\n let screenshotPath: string | undefined;\n\n if (input.screenshotDataUrl) {\n const saved = this.screenshotStore.saveScreenshot(input.projectId, incidentId, 'error', input.screenshotDataUrl);\n if (saved) {\n screenshotPath = saved.filePath;\n }\n }\n\n const newIncident: Incident = {\n id: incidentId,\n projectId: input.projectId,\n sessionId: input.sessionId,\n type: input.type,\n severity: input.severity,\n message: input.message,\n source: {\n file: sourceFile,\n line,\n column,\n },\n fingerprint,\n route: input.route,\n firstSeen: now,\n lastSeen: now,\n occurrences: 1,\n status: 'OPEN',\n stack: input.stack,\n breadcrumbs: input.breadcrumbs,\n networkFailures: input.networkFailures,\n lastElement: input.lastElement,\n screenshots: screenshotPath ? { error: screenshotPath } : undefined,\n };\n\n this.db.insertIncident(newIncident);\n\n // Initial occurrence\n this.db.insertIncidentOccurrence({\n id: `occ_${crypto.randomUUID().slice(0, 8)}`,\n incidentId,\n sessionId: input.sessionId,\n timestamp: now,\n route: input.route,\n url: input.url,\n stack: input.stack,\n breadcrumbs: input.breadcrumbs,\n lastElement: input.lastElement,\n });\n\n return newIncident;\n }\n}\n","import fs from 'node:fs';\nimport http from 'node:http';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { StorageDB } from '../storage/db.js';\nimport type { SessionManager } from '../session/manager.js';\n\nexport function createHttpHandler(db: StorageDB, sessionManager: SessionManager, baseScreenshotsDir: string) {\n return (req: http.IncomingMessage, res: http.ServerResponse) => {\n const parsedUrl = new URL(req.url || '/', `http://${req.headers.host || '127.0.0.1'}`);\n const pathname = parsedUrl.pathname;\n\n // CORS headers for local development access\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // 1. Health check\n if (pathname === '/health' || pathname === '/') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n status: 'ok',\n name: 'browsertrack',\n version: '0.1.0',\n activeSessions: sessionManager.getActiveCount(),\n timestamp: new Date().toISOString(),\n })\n );\n return;\n }\n\n // 2. Client script tag bundle\n if (pathname === '/client.js' || pathname === '/browserdiag.js') {\n let scriptContent = '';\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n // Look in dist or adjacent package\n const candidatePaths = [\n path.resolve(__dirname, '../client.iife.js'),\n path.resolve(__dirname, '../../dist/client.iife.js'),\n path.resolve(__dirname, '../../../dist/client.iife.js'),\n ];\n\n for (const p of candidatePaths) {\n if (fs.existsSync(p)) {\n scriptContent = fs.readFileSync(p, 'utf-8');\n break;\n }\n }\n } catch {}\n\n if (!scriptContent) {\n scriptContent = `console.warn(\"[BrowserTrack] Standalone client bundle not built yet. Run 'npm run build'.\");`;\n }\n\n res.writeHead(200, {\n 'Content-Type': 'application/javascript; charset=utf-8',\n 'Cache-Control': 'no-cache',\n });\n res.end(scriptContent);\n return;\n }\n\n // 3. API - Projects\n if (pathname === '/api/projects') {\n const projects = db.listProjects();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, projects }));\n return;\n }\n\n // 4. API - Sessions\n if (pathname === '/api/sessions') {\n const projectId = parsedUrl.searchParams.get('project') || undefined;\n const activeOnly = parsedUrl.searchParams.get('active') === 'true';\n const sessions = db.listSessions(projectId, activeOnly);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, sessions }));\n return;\n }\n\n // 5. API - Incidents\n if (pathname === '/api/incidents') {\n const projectId = parsedUrl.searchParams.get('project') || undefined;\n const status = (parsedUrl.searchParams.get('status') as any) || undefined;\n const limit = parseInt(parsedUrl.searchParams.get('limit') || '50', 10);\n const incidents = db.listIncidents({ projectId, status, limit });\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, incidents }));\n return;\n }\n\n // 6. API - Incident Detail\n if (pathname.startsWith('/api/incidents/')) {\n const incidentId = pathname.replace('/api/incidents/', '');\n const incident = db.getIncident(incidentId);\n if (!incident) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: false, error: 'Incident not found' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, incident }));\n return;\n }\n\n // 7. API - Notes\n if (pathname === '/api/notes') {\n const projectId = parsedUrl.searchParams.get('project') || undefined;\n const status = (parsedUrl.searchParams.get('status') as any) || undefined;\n const limit = parseInt(parsedUrl.searchParams.get('limit') || '50', 10);\n const notes = db.listNotes({ projectId, status, limit });\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, notes }));\n return;\n }\n\n // 8. API - Note Detail\n if (pathname.startsWith('/api/notes/')) {\n const noteId = pathname.replace('/api/notes/', '');\n const note = db.getNote(noteId);\n if (!note) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: false, error: 'Note not found' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, note }));\n return;\n }\n\n // 9. Screenshots Serving\n if (pathname.startsWith('/screenshots/')) {\n const relativePath = pathname.replace('/screenshots/', '');\n const filePath = path.join(baseScreenshotsDir, relativePath);\n if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {\n const ext = path.extname(filePath).toLowerCase();\n const mimeTypes: Record<string, string> = {\n '.webp': 'image/webp',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n };\n res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' });\n fs.createReadStream(filePath).pipe(res);\n return;\n }\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: false, error: 'Screenshot not found' }));\n return;\n }\n\n // Not found\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: false, error: 'Not found' }));\n };\n}\n","import crypto from 'node:crypto';\nimport type { WebSocket, WebSocketServer } from 'ws';\nimport type { ClientEventMessage, CommandResponse, HelloMessage } from '../../../core/src/index.js';\nimport type { IncidentEngine } from '../incidents/engine.js';\nimport type { NotesEngine } from '../notes/engine.js';\nimport type { SessionManager } from '../session/manager.js';\nimport type { StorageDB } from '../storage/db.js';\n\nexport function setupWebSocketServer(\n wss: WebSocketServer,\n db: StorageDB,\n sessionManager: SessionManager,\n incidentEngine: IncidentEngine,\n notesEngine?: NotesEngine,\n maxEventsPerSession = 1000,\n verbose = false\n): void {\n wss.on('connection', (ws: WebSocket) => {\n let currentSessionId: string | null = null;\n\n ws.on('message', (raw: string | Buffer) => {\n try {\n const text = typeof raw === 'string' ? raw : raw.toString('utf-8');\n const data = JSON.parse(text);\n\n // 1. Hello handshake\n if (data.type === 'hello') {\n const hello = data as HelloMessage;\n const sessionId = `sess_${crypto.randomUUID().slice(0, 8)}`;\n currentSessionId = sessionId;\n\n // Determine or create project\n let project = hello.projectId ? db.getProject(hello.projectId) : null;\n if (!project && hello.origin) {\n project = db.getProjectByOrigin(hello.origin);\n }\n if (!project) {\n // Auto-create project based on origin or hostname\n let projName = 'default';\n try {\n const url = new URL(hello.origin || 'http://localhost');\n projName = url.port ? `app-${url.port}` : url.hostname;\n } catch {\n projName = 'app';\n }\n project = db.upsertProject({\n id: `proj_${crypto.randomUUID().slice(0, 8)}`,\n name: projName,\n origin: hello.origin || 'http://localhost',\n });\n }\n\n const now = new Date().toISOString();\n db.upsertSession({\n id: sessionId,\n projectId: project.id,\n origin: hello.origin || '',\n url: hello.url || '',\n title: hello.title || '',\n userAgent: hello.userAgent || '',\n connectedAt: now,\n lastSeenAt: now,\n active: true,\n });\n\n sessionManager.registerSocket(sessionId, ws, hello.origin || '', project.id);\n\n ws.send(\n JSON.stringify({\n type: 'hello_ack',\n sessionId,\n projectId: project.id,\n projectName: project.name,\n })\n );\n\n // Sync existing notes for this project on load\n const existingNotes = db.listNotes({ projectId: project.id, limit: 100 });\n ws.send(\n JSON.stringify({\n type: 'notes_sync',\n notes: existingNotes,\n })\n );\n\n if (verbose) {\n console.log(`[BrowserTrack] New session connected: ${sessionId} (${project.name} @ ${hello.origin})`);\n }\n return;\n }\n\n // 2. Client Event (runtime error, console, network, breadcrumb)\n if (data.type === 'event') {\n const eventMsg = data as ClientEventMessage;\n const sessionId = eventMsg.sessionId || currentSessionId;\n if (!sessionId) return;\n\n const eventId = `evt_${crypto.randomUUID().slice(0, 8)}`;\n db.insertEvent({\n id: eventId,\n sessionId,\n eventType: eventMsg.eventType,\n payload: eventMsg.payload,\n timestamp: eventMsg.timestamp || Date.now(),\n route: eventMsg.route,\n url: eventMsg.url,\n });\n\n // Session retention prune\n db.pruneSessionEvents(sessionId, maxEventsPerSession);\n\n // Incident processing\n const incident = incidentEngine.processClientEvent(eventMsg);\n if (incident && verbose) {\n console.log(\n `[BrowserTrack] Incident recorded: ${incident.id} (${incident.type}: ${incident.message}) [${incident.occurrences}x]`\n );\n }\n return;\n }\n\n // 3. Visual Note Creation\n if (data.type === 'create_note' && notesEngine) {\n const note = notesEngine.createNoteFromClient({\n sessionId: data.sessionId || currentSessionId || '',\n noteType: data.noteType,\n message: data.message,\n route: data.route,\n url: data.url,\n viewport: data.viewport,\n scroll: data.scroll,\n target: data.target,\n elementContext: data.elementContext,\n region: data.region,\n screenshot: data.screenshot,\n incidentId: data.incidentId,\n scenarioId: data.scenarioId,\n stepNumber: data.stepNumber,\n scenarioTitle: data.scenarioTitle,\n });\n\n if (verbose) {\n console.log(\n `[BrowserTrack] Visual note created: ${note.id} on ${note.route} (\"${note.message}\")${\n note.scenarioId ? ` [Scenario: ${note.scenarioTitle || note.scenarioId} Step ${note.stepNumber}]` : ''\n }`\n );\n }\n\n ws.send(\n JSON.stringify({\n type: 'note_created_ack',\n noteId: note.id,\n status: note.status,\n scenarioId: note.scenarioId,\n stepNumber: note.stepNumber,\n })\n );\n\n // Broadcast updated notes to all active browser tabs in this project\n const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });\n sessionManager.broadcastToProject(note.projectId, {\n type: 'notes_sync',\n notes: allNotes,\n });\n return;\n }\n\n // 4. Visual Note Status / Delete Actions\n if (data.type === 'resolve_note' && data.noteId) {\n const note = db.getNote(data.noteId);\n if (note) {\n db.updateNoteStatus(data.noteId, 'RESOLVED');\n const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });\n sessionManager.broadcastToProject(note.projectId, {\n type: 'notes_sync',\n notes: allNotes,\n });\n }\n return;\n }\n\n if (data.type === 'reopen_note' && data.noteId) {\n const note = db.getNote(data.noteId);\n if (note) {\n db.updateNoteStatus(data.noteId, 'OPEN');\n const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });\n sessionManager.broadcastToProject(note.projectId, {\n type: 'notes_sync',\n notes: allNotes,\n });\n }\n return;\n }\n\n if (data.type === 'delete_note' && data.noteId) {\n const note = db.getNote(data.noteId);\n if (note) {\n db.deleteNote(data.noteId);\n const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });\n sessionManager.broadcastToProject(note.projectId, {\n type: 'notes_sync',\n notes: allNotes,\n });\n }\n return;\n }\n\n if (data.type === 'delete_scenario' && data.scenarioId) {\n const scenario = db.getScenario(data.scenarioId);\n if (scenario) {\n db.deleteScenario(data.scenarioId);\n const allNotes = db.listNotes({ projectId: scenario.projectId, limit: 100 });\n sessionManager.broadcastToProject(scenario.projectId, {\n type: 'notes_sync',\n notes: allNotes,\n });\n }\n return;\n }\n\n if (data.type === 'get_notes') {\n const session = currentSessionId ? db.getSession(currentSessionId) : null;\n const projectId = data.projectId || session?.projectId;\n if (projectId) {\n const allNotes = db.listNotes({ projectId, limit: 100 });\n ws.send(\n JSON.stringify({\n type: 'notes_sync',\n notes: allNotes,\n })\n );\n }\n return;\n }\n\n // 5. Command Response\n if (data.type === 'command_response') {\n const res = data.response as CommandResponse;\n const sessionId = data.sessionId || currentSessionId;\n if (sessionId && res) {\n sessionManager.handleCommandResponse(sessionId, res);\n }\n return;\n }\n } catch (err: any) {\n if (verbose) {\n console.error('[BrowserTrack] WebSocket message error:', err?.message);\n }\n }\n });\n\n ws.on('close', () => {\n if (currentSessionId) {\n sessionManager.unregisterSocket(currentSessionId);\n if (verbose) {\n console.log(`[BrowserTrack] Session disconnected: ${currentSessionId}`);\n }\n }\n });\n\n ws.on('error', () => {\n if (currentSessionId) {\n sessionManager.unregisterSocket(currentSessionId);\n }\n });\n });\n}\n","import http from 'node:http';\nimport { WebSocketServer } from 'ws';\nimport type { DaemonConfig } from '../config.js';\nimport { getDaemonConfig } from '../config.js';\nimport { IncidentEngine } from '../incidents/engine.js';\nimport { NotesEngine } from '../notes/engine.js';\nimport { NoteVerificationEngine } from '../notes/verification.js';\nimport { SessionManager } from '../session/manager.js';\nimport { StorageDB } from '../storage/db.js';\nimport { ScreenshotStore } from '../storage/screenshot-store.js';\nimport { VerificationEngine } from '../verification/engine.js';\nimport { createHttpHandler } from './http.js';\nimport { setupWebSocketServer } from './ws.js';\n\nexport class BrowserTrackDaemon {\n public config: DaemonConfig;\n public db: StorageDB;\n public screenshotStore: ScreenshotStore;\n public sessionManager: SessionManager;\n public incidentEngine: IncidentEngine;\n public notesEngine: NotesEngine;\n public verificationEngine: VerificationEngine;\n public noteVerificationEngine: NoteVerificationEngine;\n\n private httpServer: http.Server | null = null;\n private wss: WebSocketServer | null = null;\n private isRunning = false;\n\n constructor(config: Partial<DaemonConfig> = {}) {\n this.config = getDaemonConfig(config);\n this.db = new StorageDB(this.config.dbPath);\n this.screenshotStore = new ScreenshotStore(this.config.screenshotsDir);\n this.sessionManager = new SessionManager(this.db);\n this.incidentEngine = new IncidentEngine(this.db, this.screenshotStore);\n this.notesEngine = new NotesEngine(this.db, this.screenshotStore);\n this.verificationEngine = new VerificationEngine(this.db, this.sessionManager, this.screenshotStore);\n this.noteVerificationEngine = new NoteVerificationEngine(this.db, this.sessionManager, this.notesEngine);\n }\n\n public async start(): Promise<void> {\n if (this.isRunning) return;\n\n const httpHandler = createHttpHandler(this.db, this.sessionManager, this.config.screenshotsDir);\n this.httpServer = http.createServer(httpHandler);\n\n this.wss = new WebSocketServer({ server: this.httpServer });\n setupWebSocketServer(\n this.wss,\n this.db,\n this.sessionManager,\n this.incidentEngine,\n this.notesEngine,\n this.config.maxEventsPerSession,\n this.config.verbose\n );\n\n await new Promise<void>((resolve, reject) => {\n this.httpServer!.listen(this.config.port, this.config.host, () => {\n this.isRunning = true;\n if (this.config.verbose) {\n console.log(`[BrowserTrack] Daemon running at http://${this.config.host}:${this.config.port}`);\n }\n resolve();\n });\n\n this.httpServer!.once('error', (err) => {\n reject(err);\n });\n });\n }\n\n public async stop(): Promise<void> {\n if (!this.isRunning) return;\n\n if (this.wss) {\n for (const client of this.wss.clients) {\n try {\n client.terminate();\n } catch {}\n }\n await new Promise<void>((resolve) => {\n this.wss!.close(() => resolve());\n });\n this.wss = null;\n }\n\n if (this.httpServer) {\n if (typeof (this.httpServer as any).closeAllConnections === 'function') {\n (this.httpServer as any).closeAllConnections();\n }\n await new Promise<void>((resolve) => {\n this.httpServer!.close(() => resolve());\n });\n this.httpServer = null;\n }\n\n try {\n this.db.close();\n } catch {}\n this.isRunning = false;\n }\n\n public getStatus(): { isRunning: boolean; host: string; port: number; activeSessions: number; dbPath: string } {\n return {\n isRunning: this.isRunning,\n host: this.config.host,\n port: this.config.port,\n activeSessions: this.sessionManager.getActiveCount(),\n dbPath: this.config.dbPath,\n };\n }\n}\n","export * from './config.js';\nexport * from './storage/db.js';\nexport * from './storage/screenshot-store.js';\nexport * from './session/manager.js';\nexport * from './incidents/engine.js';\nexport * from './notes/engine.js';\nexport * from './notes/verification.js';\nexport * from './verification/engine.js';\nexport * from './server/http.js';\nexport * from './server/ws.js';\nexport * from './server/daemon.js';\n\nimport { BrowserTrackDaemon } from './server/daemon.js';\nimport type { DaemonConfig } from './config.js';\n\nexport function createDaemon(config: Partial<DaemonConfig> = {}): BrowserTrackDaemon {\n return new BrowserTrackDaemon(config);\n}\n"],"mappings":"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","names":["crypto"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/mcp/src/tools.ts","../packages/mcp/src/handlers.ts","../packages/mcp/src/server.ts"],"sourcesContent":["import type { Tool } from '@modelcontextprotocol/sdk/types.js';\n\nexport const TOOLS: Tool[] = [\n {\n name: 'list_projects',\n description: 'List all registered projects tracked by BrowserTrack/BrowserDiag',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n {\n name: 'list_sessions',\n description: 'List active and recent browser sessions connected to the local development daemon',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter sessions by project ID or name' },\n activeOnly: { type: 'boolean', description: 'Show only currently active WebSocket sessions (default: true)' },\n },\n },\n },\n {\n name: 'list_incidents',\n description: 'List recorded browser runtime errors, unhandled rejections, and console errors grouped into incidents',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'FIX_ATTEMPTED', 'VERIFYING', 'VERIFIED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by incident status',\n },\n severity: {\n type: 'string',\n enum: ['error', 'warn', 'fatal'],\n description: 'Filter by severity',\n },\n limit: { type: 'number', description: 'Maximum number of incidents to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_incident',\n description: 'Retrieve compact and high-signal debugging context for a specific error incident (stack trace, breadcrumbs, network failures, last interacted element, error screenshot)',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The unique ID of the incident (e.g. inc_42)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_console',\n description: 'Get recent console logs, warnings, and errors from a browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional, defaults to active session)' },\n limit: { type: 'number', description: 'Number of console logs to retrieve (default: 30)' },\n },\n },\n },\n {\n name: 'get_network_failures',\n description: 'Get recent failed HTTP network requests (4xx, 5xx, network errors, timeouts, aborts)',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n limit: { type: 'number', description: 'Maximum number of failed requests to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_breadcrumbs',\n description: 'Get the sequence of recent user interactions, navigations, console logs, and network events prior to an error',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n incidentId: { type: 'string', description: 'Incident ID to fetch breadcrumbs from (optional)' },\n limit: { type: 'number', description: 'Maximum number of breadcrumbs (default: 50)' },\n },\n },\n },\n {\n name: 'get_page_state',\n description: 'Query the live page state (URL, route, document title, readyState, active element) from the connected browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Target session ID (optional, defaults to active)' },\n },\n },\n },\n {\n name: 'capture_element',\n description: 'Capture a screenshot of a specific DOM element or the entire visible page in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the element to capture (e.g. [data-testid=\"user-card\"])' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n },\n },\n {\n name: 'verify_incident',\n description: 'Trigger closed-loop verification of a bug fix: reloads the browser, checks if the incident reoccurs, evaluates optional verification probes, and records before/after screenshots',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident to verify' },\n route: { type: 'string', description: 'Optional route to navigate to for verification (defaults to incident route)' },\n targetSelector: { type: 'string', description: 'Optional element selector to inspect and capture after-fix screenshot' },\n expect: {\n type: 'array',\n description: 'Optional verification probes to evaluate after reload',\n items: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: ['element_exists', 'element_visible', 'text_contains', 'route_is', 'no_incident'],\n },\n selector: { type: 'string' },\n text: { type: 'string' },\n route: { type: 'string' },\n },\n required: ['type'],\n },\n },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 2000)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_verification',\n description: 'Retrieve the latest verification result and before/after screenshot artifacts for an incident',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'list_notes',\n description: 'List visual development notes / screen annotations left on elements, regions, or pages during development',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter notes by project ID or name' },\n scenarioId: { type: 'string', description: 'Filter notes by scenario / flow ID' },\n status: {\n type: 'string',\n enum: ['OPEN', 'IN_PROGRESS', 'VERIFYING', 'RESOLVED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by note status (default: OPEN)',\n },\n limit: { type: 'number', description: 'Maximum number of notes to return (default: 20)' },\n },\n },\n },\n {\n name: 'list_scenarios',\n description: 'List recorded reproduction scenarios and multi-step user interaction flows',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter scenarios by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'RESOLVED'],\n description: 'Filter by scenario status',\n },\n limit: { type: 'number', description: 'Maximum number of scenarios to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_scenario',\n description: 'Retrieve full chronological step-by-step reproduction flow with selectors, route, screenshots, and action details',\n inputSchema: {\n type: 'object',\n properties: {\n scenarioId: { type: 'string', description: 'The unique ID of the scenario / flow (e.g. scen_123)' },\n },\n required: ['scenarioId'],\n },\n },\n {\n name: 'get_note',\n description: 'Retrieve full debugging context for a visual note (message, route, viewport dimensions, target element selector, DOM context, screenshot file path, project path)',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The unique ID of the visual note (e.g. note_42)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'resolve_note',\n description: 'Mark a visual note as RESOLVED once the layout or styling issue has been fixed',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to resolve' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'reopen_note',\n description: 'Reopen a previously resolved visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to reopen' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'verify_note',\n description: 'Run closed-loop layout & visual verification for a note: checks route, element existence/visibility, evaluates viewport overflow probes, captures after-screenshot, and computes geometry diff',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to verify' },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 1000)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'get_note_verification',\n description: 'Retrieve the latest verification result, before/after screenshot references, and layout geometry diff for a visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'capture_note_context',\n description: 'Inspect live DOM context, bounding box, overflow, and styles for a target element in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the target element' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n required: ['selector'],\n },\n },\n];\n","import type { StorageDB } from '../../daemon/src/storage/db.js';\nimport type { VerificationEngine } from '../../daemon/src/verification/engine.js';\nimport type { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport type { SessionManager } from '../../daemon/src/session/manager.js';\n\nexport interface McpContext {\n db: StorageDB;\n sessionManager?: SessionManager;\n verificationEngine?: VerificationEngine;\n noteVerificationEngine?: NoteVerificationEngine;\n daemonUrl?: string;\n}\n\nexport async function handleToolCall(name: string, args: any, ctx: McpContext): Promise<any> {\n const { db, sessionManager, verificationEngine, noteVerificationEngine } = ctx;\n\n switch (name) {\n case 'list_projects': {\n const projects = db.listProjects();\n return {\n projects: projects.map((p) => ({\n id: p.id,\n name: p.name,\n origin: p.origin,\n path: p.path,\n updatedAt: p.updatedAt,\n })),\n };\n }\n\n case 'list_sessions': {\n const activeOnly = args.activeOnly !== false;\n const sessions = db.listSessions(args.projectId, activeOnly);\n return {\n count: sessions.length,\n sessions: sessions.map((s) => ({\n id: s.id,\n projectId: s.projectId,\n origin: s.origin,\n url: s.url,\n title: s.title,\n active: s.active,\n lastSeenAt: s.lastSeenAt,\n })),\n };\n }\n\n case 'list_incidents': {\n const incidents = db.listIncidents({\n projectId: args.projectId,\n status: args.status,\n severity: args.severity,\n limit: args.limit || 20,\n });\n return {\n total: incidents.length,\n incidents: incidents.map((inc) => ({\n id: inc.id,\n type: inc.type,\n severity: inc.severity,\n message: inc.message,\n source: `${inc.source.file}:${inc.source.line}`,\n route: inc.route,\n status: inc.status,\n occurrences: inc.occurrences,\n firstSeen: inc.firstSeen,\n lastSeen: inc.lastSeen,\n })),\n };\n }\n\n case 'get_incident': {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n\n // Compact breadcrumbs summary (last 10 most relevant)\n const breadcrumbsTimeline = incident.breadcrumbs.slice(-15).map((b) => {\n const time = new Date(b.timestamp).toISOString().split('T')[1]?.slice(0, 8);\n return `[${time}] ${b.message}`;\n });\n\n return {\n id: incident.id,\n status: incident.status,\n type: incident.type,\n severity: incident.severity,\n message: incident.message,\n source: {\n file: incident.source.file,\n line: incident.source.line,\n column: incident.source.column,\n },\n route: incident.route,\n occurrences: incident.occurrences,\n firstSeen: incident.firstSeen,\n lastSeen: incident.lastSeen,\n stack: incident.stack,\n lastInteractedElement: incident.lastElement\n ? {\n selector: incident.lastElement.selector,\n tag: incident.lastElement.tag,\n visible: incident.lastElement.visible,\n innerText: incident.lastElement.innerText,\n outerHTML: incident.lastElement.outerHTML,\n }\n : undefined,\n recentBreadcrumbs: breadcrumbsTimeline,\n networkFailures: incident.networkFailures,\n screenshot: incident.screenshots?.error,\n };\n }\n\n case 'get_console': {\n const limit = args.limit || 30;\n const events = db.getEvents({\n sessionId: args.sessionId,\n eventType: 'console',\n limit,\n });\n\n return {\n logs: events.map((e) => ({\n level: e.payload.level,\n message: e.payload.message,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_network_failures': {\n const limit = args.limit || 20;\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: 100,\n });\n\n const failures = events\n .filter((e) => (e.eventType === 'fetch' || e.eventType === 'xhr') && (e.payload.status >= 400 || e.payload.error))\n .slice(0, limit)\n .map((e) => ({\n url: e.payload.url,\n method: e.payload.method,\n status: e.payload.status,\n error: e.payload.error,\n durationMs: e.payload.durationMs,\n timestamp: new Date(e.timestamp).toISOString(),\n }));\n\n return { failures };\n }\n\n case 'get_breadcrumbs': {\n if (args.incidentId) {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n return {\n incidentId: args.incidentId,\n breadcrumbs: incident.breadcrumbs.slice(-(args.limit || 50)),\n };\n }\n\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: args.limit || 50,\n });\n\n return {\n breadcrumbs: events.map((e) => ({\n type: e.eventType,\n message: e.payload.message || `${e.eventType} on ${e.route}`,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_page_state': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const cmdRes = await sessionManager.sendCommand(session.id, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'get_page_state',\n });\n\n if (!cmdRes.ok) {\n throw new Error(cmdRes.error || 'Failed to retrieve page state from browser.');\n }\n return cmdRes.result;\n }\n\n case 'capture_element': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const cmdRes = await sessionManager.sendCommand(session.id, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'capture_element',\n params: { selector: args.selector },\n });\n\n if (!cmdRes.ok) {\n throw new Error(cmdRes.error || cmdRes.reason || 'Failed to capture element screenshot.');\n }\n return {\n ok: true,\n format: cmdRes.result?.format || 'webp',\n width: cmdRes.result?.width,\n height: cmdRes.result?.height,\n dataUrlPreview: cmdRes.result?.dataUrl ? `${cmdRes.result.dataUrl.slice(0, 100)}...` : undefined,\n };\n }\n\n case 'verify_incident': {\n if (!verificationEngine) {\n throw new Error('Verification engine not available: Daemon session manager not attached.');\n }\n\n const res = await verificationEngine.verifyIncident(args.incidentId, {\n route: args.route,\n targetSelector: args.targetSelector,\n expect: args.expect,\n observationWindowMs: args.observationWindowMs,\n });\n\n return res;\n }\n\n case 'get_verification': {\n const v = db.getLatestVerification(args.incidentId);\n if (!v) {\n throw new Error(`No verification records found for incident '${args.incidentId}'.`);\n }\n return v;\n }\n\n case 'list_notes': {\n const notes = db.listNotes({\n projectId: args.projectId,\n scenarioId: args.scenarioId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: notes.length,\n notes: notes.map((n) => ({\n id: n.id,\n type: n.type,\n status: n.status,\n route: n.route,\n scenarioId: n.scenarioId,\n stepNumber: n.stepNumber,\n scenarioTitle: n.scenarioTitle,\n viewport: `${n.viewport.width} × ${n.viewport.height}`,\n target: n.target?.selector || n.type,\n message: n.message,\n screenshotAvailable: !!n.screenshots?.original,\n createdAt: n.createdAt,\n })),\n };\n }\n\n case 'list_scenarios': {\n const scenarios = db.listScenarios({\n projectId: args.projectId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: scenarios.length,\n scenarios: scenarios.map((s) => ({\n id: s.id,\n title: s.title,\n stepsCount: s.stepsCount,\n status: s.status,\n route: s.route,\n firstStepAt: s.firstStepAt,\n lastStepAt: s.lastStepAt,\n })),\n };\n }\n\n case 'get_scenario': {\n const scenario = db.getScenario(args.scenarioId);\n if (!scenario) {\n throw new Error(`Scenario '${args.scenarioId}' not found.`);\n }\n\n return {\n id: scenario.id,\n title: scenario.title,\n stepsCount: scenario.stepsCount,\n status: scenario.status,\n steps: scenario.steps.map((step) => ({\n id: step.id,\n stepNumber: step.stepNumber,\n type: step.type,\n message: step.message,\n route: step.route,\n url: step.url,\n targetSelector: step.target?.selector,\n boundingRect: step.target?.boundingRect || step.region,\n elementContext: step.elementContext,\n screenshot: step.screenshots?.original,\n status: step.status,\n createdAt: step.createdAt,\n })),\n createdAt: scenario.createdAt,\n updatedAt: scenario.updatedAt,\n };\n }\n\n case 'get_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n\n const project = db.getProject(note.projectId);\n\n return {\n id: note.id,\n type: note.type,\n status: note.status,\n message: note.message,\n scenarioId: note.scenarioId,\n stepNumber: note.stepNumber,\n scenarioTitle: note.scenarioTitle,\n route: note.route,\n url: note.url,\n viewport: note.viewport,\n scroll: note.scroll,\n target: note.target,\n elementContext: note.elementContext,\n region: note.region,\n screenshot: {\n available: !!note.screenshots?.original,\n original: note.screenshots?.original,\n after: note.screenshots?.after,\n },\n project: project\n ? {\n id: project.id,\n name: project.name,\n origin: project.origin,\n path: project.path,\n }\n : undefined,\n relatedIncidentId: note.incidentId,\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n resolvedAt: note.resolvedAt,\n };\n }\n\n case 'resolve_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'RESOLVED');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'RESOLVED',\n resolvedAt: new Date().toISOString(),\n };\n }\n\n case 'reopen_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'OPEN');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'OPEN',\n };\n }\n\n case 'verify_note': {\n if (!noteVerificationEngine) {\n throw new Error('Note verification engine not available: Daemon session manager not attached.');\n }\n const res = await noteVerificationEngine.verifyNote(args.noteId, {\n observationWindowMs: args.observationWindowMs,\n });\n return res;\n }\n\n case 'get_note_verification': {\n const v = db.getLatestNoteVerification(args.noteId);\n if (!v) {\n throw new Error(`No verification records found for visual note '${args.noteId}'.`);\n }\n return v;\n }\n\n case 'capture_note_context': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const queryCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_ctx_${Date.now()}`,\n type: 'query_element',\n params: { selector: args.selector },\n });\n\n const overflowCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_ovf_${Date.now()}`,\n type: 'check_overflow',\n params: { selector: args.selector },\n });\n\n const styleCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_sty_${Date.now()}`,\n type: 'get_element_style',\n params: { selector: args.selector },\n });\n\n return {\n selector: args.selector,\n element: queryCmd.result,\n overflow: overflowCmd.result,\n styles: styleCmd.result?.styles,\n };\n }\n\n default:\n throw new Error(`Unknown MCP tool: ${name}`);\n }\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { getDaemonConfig } from '../../daemon/src/config.js';\nimport { StorageDB } from '../../daemon/src/storage/db.js';\nimport { ScreenshotStore } from '../../daemon/src/storage/screenshot-store.js';\nimport { SessionManager } from '../../daemon/src/session/manager.js';\nimport { NotesEngine } from '../../daemon/src/notes/engine.js';\nimport { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport { VerificationEngine } from '../../daemon/src/verification/engine.js';\nimport type { McpContext } from './handlers.js';\nimport { handleToolCall } from './handlers.js';\nimport { TOOLS } from './tools.js';\n\nexport interface McpServerOptions {\n dbPath?: string;\n context?: Partial<McpContext>;\n}\n\nexport function createMcpServer(options: McpServerOptions = {}) {\n const config = getDaemonConfig({ dbPath: options.dbPath });\n const db = options.context?.db || new StorageDB(config.dbPath);\n const screenshotStore = new ScreenshotStore(config.screenshotsDir);\n const sessionManager = options.context?.sessionManager || new SessionManager(db);\n const verificationEngine = options.context?.verificationEngine || new VerificationEngine(db, sessionManager, screenshotStore);\n const notesEngine = new NotesEngine(db, screenshotStore);\n const noteVerificationEngine = options.context?.noteVerificationEngine || new NoteVerificationEngine(db, sessionManager, notesEngine);\n\n const ctx: McpContext = {\n db,\n sessionManager,\n verificationEngine,\n noteVerificationEngine,\n daemonUrl: `http://${config.host}:${config.port}`,\n ...options.context,\n };\n\n const server = new Server(\n {\n name: 'browsertrack-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: TOOLS };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n try {\n const result = await handleToolCall(name, args || {}, ctx);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (err: any) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: `Error executing tool ${name}: ${err?.message || String(err)}`,\n },\n ],\n };\n }\n });\n\n return {\n server,\n async startStdio() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n"],"mappings":"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","names":[]}