browsertrack 0.1.0 → 0.1.1
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.
- package/.github/workflows/docs.yml +54 -0
- package/AGENTS.md +133 -0
- package/README.md +1 -1
- package/dist/{chunk-6A7FFDIB.js → chunk-6VA7GBAO.js} +29 -4
- package/dist/chunk-6VA7GBAO.js.map +1 -0
- package/dist/{chunk-ANMR5WBH.js → chunk-G2Y3CXCY.js} +2 -2
- package/dist/{chunk-BSKNG4V7.js → chunk-ILRYKMME.js} +30 -1
- package/dist/chunk-ILRYKMME.js.map +1 -0
- package/dist/{chunk-X7C5CHBO.js → chunk-SKCMT2DE.js} +637 -76
- package/dist/chunk-SKCMT2DE.js.map +1 -0
- package/dist/{chunk-ZLNGOOH2.js → chunk-SPCIROIU.js} +65 -3
- package/dist/chunk-SPCIROIU.js.map +1 -0
- package/dist/cli/index.js +115 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +637 -75
- package/dist/client/index.d.ts +16 -2
- package/dist/client/index.js +2 -2
- package/dist/client.iife.js +393 -72
- package/dist/core/index.d.ts +12 -4
- package/dist/core/index.js +9 -3
- package/dist/daemon/index.d.ts +1 -1
- package/dist/daemon/index.js +3 -3
- package/dist/{engine-DKloFjvs.d.ts → engine-CeT9URuN.d.ts} +4 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +13 -7
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/{server-CN-se8td.d.ts → server-Dd8NX2Mk.d.ts} +1 -1
- package/docboot.config.js +16 -0
- package/docs/cli.md +51 -0
- package/docs/closed-loop-verification.md +57 -0
- package/docs/getting-started.md +122 -0
- package/docs/incidents-diagnostics.md +61 -0
- package/docs/index.md +46 -0
- package/docs/mcp-reference.md +128 -0
- package/docs/security-privacy.md +32 -0
- package/docs/visual-notes.md +67 -0
- package/package.json +6 -2
- package/packages/client/package.json +2 -2
- package/packages/client/src/notes/inspector.ts +689 -79
- package/packages/client/src/transport/websocket.ts +15 -0
- package/packages/core/package.json +3 -0
- package/packages/core/src/redaction.ts +40 -5
- package/packages/daemon/src/server/ws.ts +72 -1
- package/packages/daemon/src/session/manager.ts +21 -0
- package/packages/daemon/src/storage/db.ts +10 -1
- package/test/client/interceptors.test.ts +61 -0
- package/test/core/redaction.test.ts +12 -0
- package/dist/chunk-6A7FFDIB.js.map +0 -1
- package/dist/chunk-BSKNG4V7.js.map +0 -1
- package/dist/chunk-X7C5CHBO.js.map +0 -1
- package/dist/chunk-ZLNGOOH2.js.map +0 -1
- package/packages/core/dist/index.d.ts +0 -317
- package/packages/core/dist/index.js +0 -221
- /package/dist/{chunk-ANMR5WBH.js.map → chunk-G2Y3CXCY.js.map} +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Generated by Docboot
|
|
2
|
+
name: Deploy Docs
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: pages
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Setup Node.js
|
|
29
|
+
uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: '22'
|
|
32
|
+
cache: 'npm'
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: npm ci
|
|
36
|
+
|
|
37
|
+
- name: Build documentation
|
|
38
|
+
run: npx -y docboot build
|
|
39
|
+
|
|
40
|
+
- name: Upload Pages artifact
|
|
41
|
+
uses: actions/upload-pages-artifact@v3
|
|
42
|
+
with:
|
|
43
|
+
path: dist-docs
|
|
44
|
+
|
|
45
|
+
deploy:
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment:
|
|
49
|
+
name: github-pages
|
|
50
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
51
|
+
steps:
|
|
52
|
+
- name: Deploy to GitHub Pages
|
|
53
|
+
id: deployment
|
|
54
|
+
uses: actions/deploy-pages@v4
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# BrowserTrack — AI Agent Guidelines & MCP Instructions
|
|
2
|
+
|
|
3
|
+
This repository is integrated with **BrowserTrack**, a local browser diagnostics daemon and Model Context Protocol (MCP) bridge. As an AI coding agent, you have direct access to live browser runtime errors, console logs, network failures, user breadcrumbs, visual notes, screenshots, and automated closed-loop verification tools.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🎯 When to Use BrowserTrack Tools
|
|
8
|
+
|
|
9
|
+
Always check BrowserTrack when:
|
|
10
|
+
1. **The user reports a bug, crash, or visual issue in the browser.**
|
|
11
|
+
2. **The user leaves visual annotations or design feedback** on elements/pages (Alt+Click notes).
|
|
12
|
+
3. **You modify frontend code** (React, Vue, HTML, CSS, JS) and need to verify whether runtime errors or visual regressions occurred.
|
|
13
|
+
4. **You need real-time page state, live DOM hierarchy, or screenshots** without asking the user to manually copy-paste logs or send screenshots.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🧭 Core Agent Workflows
|
|
18
|
+
|
|
19
|
+
### 1. Visual Notes & UI Fixes Workflow
|
|
20
|
+
|
|
21
|
+
When the user asks to fix a UI issue, layout bug, or style note:
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph TD
|
|
25
|
+
A[list_notes] --> B[get_note noteId]
|
|
26
|
+
B --> C[Analyze DOM Context, Viewport & Screenshot]
|
|
27
|
+
C --> D[Modify CSS / Component Code]
|
|
28
|
+
D --> E[verify_note noteId]
|
|
29
|
+
E --> F{Resolved?}
|
|
30
|
+
F -->|Yes| G[resolve_note noteId]
|
|
31
|
+
F -->|No| D
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
1. **Find open notes:** Call `list_notes({ status: "OPEN" })`.
|
|
35
|
+
2. **Inspect note context:** Call `get_note({ noteId: "note_xxx" })`.
|
|
36
|
+
- Review the target selector (`target.selector`), viewport dimensions, bounding box (`boundingRect`), and screenshot path.
|
|
37
|
+
- Check if layout overflow (`overflow-x`, min-width, flex wrapping) is causing the issue.
|
|
38
|
+
3. **Apply the fix:** Edit the corresponding CSS, style rules, or component markup.
|
|
39
|
+
4. **Verify the fix:** Call `verify_note({ noteId: "note_xxx" })`.
|
|
40
|
+
- This automatically reloads the browser, checks element visibility, tests viewport overflow, and captures an after-fix screenshot.
|
|
41
|
+
5. **Close the note:** If verified, call `resolve_note({ noteId: "note_xxx" })`.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### 2. Runtime Error & Crash Diagnostics Workflow
|
|
46
|
+
|
|
47
|
+
When an error or unexpected behavior occurs in the frontend:
|
|
48
|
+
|
|
49
|
+
1. **List open incidents:** Call `list_incidents({ status: "OPEN" })`.
|
|
50
|
+
2. **Retrieve incident details:** Call `get_incident({ incidentId: "inc_xxx" })`.
|
|
51
|
+
- Inspect stack traces, exact source line/column, last user interactions, and failing network calls.
|
|
52
|
+
3. **Inspect interaction timeline (if needed):** Call `get_breadcrumbs({ incidentId: "inc_xxx" })` to see what clicks, inputs, or navigations led to the failure.
|
|
53
|
+
4. **Inspect failed network requests (if needed):** Call `get_network_failures()` to examine 4xx/5xx status codes, request URLs, and response details.
|
|
54
|
+
5. **Apply code fix:** Edit the source file causing the bug.
|
|
55
|
+
6. **Trigger closed-loop verification:** Call `verify_incident({ incidentId: "inc_xxx" })`.
|
|
56
|
+
- Optionally supply `expect` probes (e.g. `[{ type: "no_incident" }, { type: "element_visible", selector: ".my-component" }]`).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🛠️ Complete MCP Tool Reference
|
|
61
|
+
|
|
62
|
+
### 📋 Incidents & Diagnostics
|
|
63
|
+
| MCP Tool | Purpose | Key Arguments |
|
|
64
|
+
| :--- | :--- | :--- |
|
|
65
|
+
| `list_incidents` | List grouped browser runtime errors and unhandled rejections | `status`, `severity`, `projectId`, `limit` |
|
|
66
|
+
| `get_incident` | Retrieve stack trace, breadcrumbs, screenshot, and context for an incident | `incidentId` *(required)* |
|
|
67
|
+
| `get_console` | Fetch recent console errors, warnings, and logs | `sessionId`, `limit` |
|
|
68
|
+
| `get_network_failures` | Fetch recent 4xx/5xx HTTP failures and aborted requests | `sessionId`, `limit` |
|
|
69
|
+
| `get_breadcrumbs` | Fetch chronological user interaction and navigation history | `incidentId`, `sessionId`, `limit` |
|
|
70
|
+
| `get_page_state` | Query current URL, document title, readyState, and active element | `sessionId` |
|
|
71
|
+
| `capture_element` | Capture an on-demand screenshot of a selector or full viewport | `selector`, `sessionId` |
|
|
72
|
+
|
|
73
|
+
### 📝 Visual Notes & Layout Inspector
|
|
74
|
+
| MCP Tool | Purpose | Key Arguments |
|
|
75
|
+
| :--- | :--- | :--- |
|
|
76
|
+
| `list_notes` | List visual annotations left on elements/regions | `status`, `projectId`, `limit` |
|
|
77
|
+
| `get_note` | Get full note details (DOM context, selector, bounding rect, screenshot) | `noteId` *(required)* |
|
|
78
|
+
| `capture_note_context` | Inspect live DOM bounding box, styles, and overflow on an element | `selector` *(required)*, `sessionId` |
|
|
79
|
+
| `verify_note` | Run automated layout verification and before/after comparison | `noteId` *(required)*, `observationWindowMs` |
|
|
80
|
+
| `get_note_verification`| Retrieve latest layout verification verdict and screenshots | `noteId` *(required)* |
|
|
81
|
+
| `resolve_note` | Mark a visual note as RESOLVED | `noteId` *(required)* |
|
|
82
|
+
| `reopen_note` | Reopen a previously resolved visual note | `noteId` *(required)* |
|
|
83
|
+
|
|
84
|
+
### 🎛️ Session & Project Management
|
|
85
|
+
| MCP Tool | Purpose | Key Arguments |
|
|
86
|
+
| :--- | :--- | :--- |
|
|
87
|
+
| `list_projects` | List all registered projects and mapped paths | *(none)* |
|
|
88
|
+
| `list_sessions` | List active browser tabs connected via WebSocket | `projectId`, `activeOnly` |
|
|
89
|
+
| `verify_incident` | Verify bug fix by reloading browser and evaluating probes | `incidentId` *(required)*, `expect`, `route` |
|
|
90
|
+
| `get_verification` | Retrieve verification verdict (`VERIFIED`, `FAILED`, `INCONCLUSIVE`) | `incidentId` *(required)* |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## ⚡ Quick Setup for Projects
|
|
95
|
+
|
|
96
|
+
If BrowserTrack is not yet active in your project:
|
|
97
|
+
|
|
98
|
+
### 1. Ensure Daemon is Running
|
|
99
|
+
```bash
|
|
100
|
+
browsertrack start
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2. Connect the Browser Client
|
|
104
|
+
- **Via HTML script tag:**
|
|
105
|
+
```html
|
|
106
|
+
<script src="http://127.0.0.1:7331/client.js"></script>
|
|
107
|
+
```
|
|
108
|
+
- **Or via NPM package:**
|
|
109
|
+
```typescript
|
|
110
|
+
import 'browsertrack/client';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 3. MCP Configuration
|
|
114
|
+
Ensure BrowserTrack MCP is configured in your editor:
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"mcpServers": {
|
|
118
|
+
"browsertrack": {
|
|
119
|
+
"command": "browsertrack",
|
|
120
|
+
"args": ["mcp"]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 💡 Best Practices for AI Agents
|
|
129
|
+
|
|
130
|
+
- **Don't guess error causes:** Always check `get_incident` or `get_breadcrumbs` before modifying code.
|
|
131
|
+
- **Verify after fixing:** Call `verify_incident` or `verify_note` after code edits to confirm the fix works in the real browser.
|
|
132
|
+
- **Respect privacy redaction:** Passwords, auth tokens, and sensitive query parameters are redacted by BrowserTrack by design.
|
|
133
|
+
- **Check responsive viewports:** When resolving layout notes, observe the `viewport` dimensions reported in `get_note`.
|
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ browsertrack mcp # Run MCP server over stdio
|
|
|
110
110
|
## 🛡️ Security & Privacy
|
|
111
111
|
|
|
112
112
|
- **Localhost Only**: Daemon strictly binds to `127.0.0.1`.
|
|
113
|
-
- **Sensitive Key Redaction**: `Authorization`, `Cookie`, `Set-Cookie`, passwords, API tokens, and secret parameters are automatically sanitized.
|
|
113
|
+
- **Sensitive Key Redaction**: Powered by `@visulima/redact` — `Authorization`, `Cookie`, `Set-Cookie`, passwords, API tokens, bearer tokens, JWTs, AWS credentials, credit cards, SSNs, and secret query parameters are automatically sanitized.
|
|
114
114
|
- **No Form Values**: Form inputs (`<input>`, `<textarea>`) are never inspected or collected.
|
|
115
115
|
- **Zero Request/Response Body**: Payload bodies are disabled by default.
|
|
116
116
|
|
|
@@ -58,6 +58,8 @@ function computeFingerprint(input) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// packages/core/src/redaction.ts
|
|
61
|
+
import { redact as visulimaRedact, standardRules } from "@visulima/redact";
|
|
62
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
61
63
|
var SENSITIVE_KEY_PATTERNS = [
|
|
62
64
|
/^authorization$/i,
|
|
63
65
|
/^cookie$/i,
|
|
@@ -87,7 +89,23 @@ var SENSITIVE_QUERY_PARAMS = [
|
|
|
87
89
|
"code",
|
|
88
90
|
"signature"
|
|
89
91
|
];
|
|
90
|
-
var
|
|
92
|
+
var BROWSER_SECURITY_RULES = [
|
|
93
|
+
{ deep: true, key: "password", replacement: REDACTED_PLACEHOLDER },
|
|
94
|
+
{ deep: true, key: "secret", replacement: REDACTED_PLACEHOLDER },
|
|
95
|
+
{ deep: true, key: "token", replacement: REDACTED_PLACEHOLDER },
|
|
96
|
+
{ deep: true, key: "authorization", replacement: REDACTED_PLACEHOLDER },
|
|
97
|
+
{ deep: true, key: "cookie", replacement: REDACTED_PLACEHOLDER },
|
|
98
|
+
{ deep: true, key: "set-cookie", replacement: REDACTED_PLACEHOLDER },
|
|
99
|
+
{ deep: true, key: "apikey", replacement: REDACTED_PLACEHOLDER },
|
|
100
|
+
{ deep: true, key: "api_key", replacement: REDACTED_PLACEHOLDER },
|
|
101
|
+
{ deep: true, key: "creditcard", pattern: /(?:\d[ -]*?){13,16}/, replacement: REDACTED_PLACEHOLDER },
|
|
102
|
+
{ deep: true, key: "cvv", replacement: REDACTED_PLACEHOLDER },
|
|
103
|
+
{ deep: true, key: "ssn", pattern: /\b\d{3}-\d{2}-\d{4}\b/, replacement: REDACTED_PLACEHOLDER },
|
|
104
|
+
{ deep: true, key: "awsid", pattern: /\bAKIA[0-9A-Z]{16}\b/, replacement: REDACTED_PLACEHOLDER },
|
|
105
|
+
{ deep: true, key: "awskey", pattern: /\b[0-9a-zA-Z/+]{40}\b/, replacement: REDACTED_PLACEHOLDER },
|
|
106
|
+
{ deep: true, key: "jwt", pattern: /\beyJ[0-9a-zA-Z_\-]*\.[0-9a-zA-Z_\-]*\.[0-9a-zA-Z_\-]*\b/, replacement: REDACTED_PLACEHOLDER },
|
|
107
|
+
{ deep: true, key: "slack_token", pattern: /\bxox[baprs]-[0-9a-zA-Z]{10,48}\b/, replacement: REDACTED_PLACEHOLDER }
|
|
108
|
+
];
|
|
91
109
|
function isSensitiveKey(key) {
|
|
92
110
|
if (!key) return false;
|
|
93
111
|
const cleaned = key.replace(/[-_]/g, "");
|
|
@@ -154,7 +172,11 @@ function redactSensitiveData(data, maxDepth = 6, currentDepth = 0) {
|
|
|
154
172
|
if (value.startsWith("http://") || value.startsWith("https://") || value.includes("?")) {
|
|
155
173
|
result[key] = redactUrl(value);
|
|
156
174
|
} else {
|
|
157
|
-
|
|
175
|
+
try {
|
|
176
|
+
result[key] = visulimaRedact(value, BROWSER_SECURITY_RULES);
|
|
177
|
+
} catch {
|
|
178
|
+
result[key] = value;
|
|
179
|
+
}
|
|
158
180
|
}
|
|
159
181
|
} else {
|
|
160
182
|
result[key] = value;
|
|
@@ -208,9 +230,12 @@ export {
|
|
|
208
230
|
normalizeSourceFile,
|
|
209
231
|
extractSourceFromStack,
|
|
210
232
|
computeFingerprint,
|
|
233
|
+
visulimaRedact,
|
|
234
|
+
standardRules,
|
|
235
|
+
REDACTED_PLACEHOLDER,
|
|
211
236
|
SENSITIVE_KEY_PATTERNS,
|
|
212
237
|
SENSITIVE_QUERY_PARAMS,
|
|
213
|
-
|
|
238
|
+
BROWSER_SECURITY_RULES,
|
|
214
239
|
isSensitiveKey,
|
|
215
240
|
redactUrl,
|
|
216
241
|
redactHeaders,
|
|
@@ -218,4 +243,4 @@ export {
|
|
|
218
243
|
getSemanticSelector,
|
|
219
244
|
truncate
|
|
220
245
|
};
|
|
221
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-6VA7GBAO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/core/src/fingerprint.ts","../packages/core/src/redaction.ts","../packages/core/src/selector.ts"],"sourcesContent":["/**\n * Error fingerprinting and normalization utilities\n */\n\nexport interface FingerprintInput {\n type: string;\n message: string;\n sourceFile?: string;\n line?: number;\n column?: number;\n stack?: string;\n}\n\n/**\n * Normalizes error messages by stripping dynamic content like UUIDs, timestamps,\n * hex addresses, and file timestamps.\n */\nexport function normalizeErrorMessage(message: string): string {\n if (!message) return 'unknown_error';\n\n return message\n // Normalize string representation\n .trim()\n // Replace hex addresses (e.g. 0x7ffee4b2a8)\n .replace(/0x[0-9a-fA-F]+/g, '0x<HEX>')\n // Replace UUIDs\n .replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, '<UUID>')\n // Replace timestamp query params like ?t=1719283748 or ?v=1.2.3\n .replace(/\\?[tv]=[\\w.-]+/g, '')\n // Replace ISO timestamps\n .replace(/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?/g, '<TIMESTAMP>')\n // Replace numbers in quotes/brackets if likely IDs (e.g., user \"12345\")\n .replace(/#\\d+/g, '#<ID>')\n // Normalize whitespaces\n .replace(/\\s+/g, ' ');\n}\n\n/**\n * Normalizes source filenames by removing origin and cache-busting query strings.\n */\nexport function normalizeSourceFile(filename?: string): string {\n if (!filename) return 'unknown_source';\n\n let cleaned = filename.trim();\n // Strip protocol and origin if present (e.g. http://localhost:5173/src/App.tsx?t=123)\n try {\n if (cleaned.startsWith('http://') || cleaned.startsWith('https://')) {\n const url = new URL(cleaned);\n cleaned = url.pathname;\n }\n } catch {\n // fallback string manipulation\n cleaned = cleaned.replace(/^https?:\\/\\/[^/]+/, '');\n }\n\n // Remove query parameters\n cleaned = cleaned.split('?')[0].split('#')[0];\n\n // Normalize windows path separators\n cleaned = cleaned.replace(/\\\\/g, '/');\n\n return cleaned || 'unknown_source';\n}\n\n/**\n * Extracts source location from stack if not explicitly provided\n */\nexport function extractSourceFromStack(stack?: string): { file: string; line: number; column?: number } | null {\n if (!stack) return null;\n\n const lines = stack.split('\\n');\n for (const line of lines) {\n // Match standard stack line: at functionName (http://localhost:5173/src/main.tsx:15:3) or at http://...:15:3\n const match = line.match(/(?:at\\s+(?:.*?\\s+\\()?)?(https?:\\/\\/[^\\s)]+|file:\\/\\/[^\\s)]+|\\/[^\\s)]+):(\\d+):(\\d+)\\)?/);\n if (match) {\n return {\n file: normalizeSourceFile(match[1]),\n line: parseInt(match[2], 10),\n column: parseInt(match[3], 10),\n };\n }\n }\n return null;\n}\n\n/**\n * Fast synchronous djb2-based hash for universal client/server runtime\n */\nfunction djb2Hash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n // Convert to unsigned 32-bit hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Computes a deterministic fingerprint for an error\n */\nexport function computeFingerprint(input: FingerprintInput): string {\n const normType = (input.type || 'Error').trim().toLowerCase();\n const normMsg = normalizeErrorMessage(input.message);\n\n let sourceFile = normalizeSourceFile(input.sourceFile);\n let line = input.line || 0;\n\n if ((sourceFile === 'unknown_source' || line === 0) && input.stack) {\n const extracted = extractSourceFromStack(input.stack);\n if (extracted) {\n sourceFile = extracted.file;\n line = extracted.line;\n }\n }\n\n const rawKey = `${normType}::${normMsg}::${sourceFile}::${line}`;\n const hash = djb2Hash(rawKey);\n\n return `fp_${hash}`;\n}\n","import { redact as visulimaRedact, standardRules, type Rules } from '@visulima/redact';\n\n/**\n * Security and Redaction Utilities powered by @visulima/redact\n */\n\nexport const REDACTED_PLACEHOLDER = '[REDACTED]';\n\nexport const SENSITIVE_KEY_PATTERNS = [\n /^authorization$/i,\n /^cookie$/i,\n /^set-cookie$/i,\n /password/i,\n /token/i,\n /secret/i,\n /api[-_]?key/i,\n /access[-_]?token/i,\n /refresh[-_]?token/i,\n /credentials/i,\n /private[-_]?key/i,\n /ssn/i,\n /credit[-_]?card/i,\n /cvv/i,\n];\n\nexport const SENSITIVE_QUERY_PARAMS = [\n 'token',\n 'auth',\n 'key',\n 'apikey',\n 'api_key',\n 'secret',\n 'password',\n 'access_token',\n 'refresh_token',\n 'code',\n 'signature',\n];\n\n/**\n * Developer security rules for @visulima/redact\n */\nexport const BROWSER_SECURITY_RULES: Rules = [\n { deep: true, key: 'password', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'secret', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'token', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'authorization', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'cookie', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'set-cookie', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'apikey', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'api_key', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'creditcard', pattern: /(?:\\d[ -]*?){13,16}/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'cvv', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'ssn', pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'awsid', pattern: /\\bAKIA[0-9A-Z]{16}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'awskey', pattern: /\\b[0-9a-zA-Z/+]{40}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'jwt', pattern: /\\beyJ[0-9a-zA-Z_\\-]*\\.[0-9a-zA-Z_\\-]*\\.[0-9a-zA-Z_\\-]*\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'slack_token', pattern: /\\bxox[baprs]-[0-9a-zA-Z]{10,48}\\b/, replacement: REDACTED_PLACEHOLDER },\n];\n\nexport { visulimaRedact, standardRules };\nexport type { Rules };\n\n/**\n * Checks if a property name matches any sensitive pattern\n */\nexport function isSensitiveKey(key: string): boolean {\n if (!key) return false;\n const cleaned = key.replace(/[-_]/g, '');\n return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(key) || pattern.test(cleaned));\n}\n\n/**\n * Redacts sensitive query parameters from a URL string\n */\nexport function redactUrl(rawUrl: string): string {\n if (!rawUrl) return rawUrl;\n\n try {\n const isRelative =\n !rawUrl.startsWith('http://') &&\n !rawUrl.startsWith('https://') &&\n !rawUrl.startsWith('ws://') &&\n !rawUrl.startsWith('wss://');\n const base = 'http://localhost';\n const parsed = new URL(rawUrl, base);\n\n let changed = false;\n for (const param of SENSITIVE_QUERY_PARAMS) {\n if (parsed.searchParams.has(param)) {\n parsed.searchParams.set(param, REDACTED_PLACEHOLDER);\n changed = true;\n }\n }\n\n // Also check all query params against isSensitiveKey\n for (const key of Array.from(parsed.searchParams.keys())) {\n if (isSensitiveKey(key)) {\n parsed.searchParams.set(key, REDACTED_PLACEHOLDER);\n changed = true;\n }\n }\n\n if (!changed) return rawUrl;\n\n let result = isRelative ? parsed.pathname + parsed.search + parsed.hash : parsed.toString();\n // Normalize URL-encoded brackets %5BREDACTED%5D back to [REDACTED]\n result = result.replace(/%5BREDACTED%5D/g, REDACTED_PLACEHOLDER);\n return result;\n } catch {\n // If URL parsing fails, perform regex replace\n let safe = rawUrl;\n for (const param of SENSITIVE_QUERY_PARAMS) {\n const reg = new RegExp(`([?&]${param}=)[^&#]+`, 'gi');\n safe = safe.replace(reg, `$1${REDACTED_PLACEHOLDER}`);\n }\n return safe;\n }\n}\n\n/**\n * Redacts sensitive HTTP headers\n */\nexport function redactHeaders(headers: Record<string, string>): Record<string, string> {\n if (!headers || typeof headers !== 'object') return {};\n\n const safe: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (isSensitiveKey(key)) {\n safe[key] = REDACTED_PLACEHOLDER;\n } else {\n safe[key] = value;\n }\n }\n return safe;\n}\n\n/**\n * Deeply redacts sensitive keys and payload data from an object or array,\n * leveraging @visulima/redact rules combined with BrowserTrack URL/header sanitizers.\n */\nexport function redactSensitiveData<T = any>(data: T, maxDepth = 6, currentDepth = 0): T {\n if (data === null || data === undefined) return data;\n if (typeof data !== 'object') return data;\n if (currentDepth > maxDepth) return '[DEPTH_EXCEEDED]' as any;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitiveData(item, maxDepth, currentDepth + 1)) as any;\n }\n\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(data as Record<string, any>)) {\n if (typeof value === 'object' && value !== null) {\n result[key] = redactSensitiveData(value, maxDepth, currentDepth + 1);\n } else if (isSensitiveKey(key)) {\n result[key] = REDACTED_PLACEHOLDER;\n } else if (typeof value === 'string') {\n // Check if value is a URL containing secrets\n if (value.startsWith('http://') || value.startsWith('https://') || value.includes('?')) {\n result[key] = redactUrl(value);\n } else {\n try {\n result[key] = visulimaRedact(value, BROWSER_SECURITY_RULES);\n } catch {\n result[key] = value;\n }\n }\n } else {\n result[key] = value;\n }\n }\n\n return result as T;\n}\n","/**\n * Semantic element selector generation and inspection\n */\n\nexport interface SelectorOptions {\n maxClasses?: number;\n maxOuterHTMLLength?: number;\n maxInnerTextLength?: number;\n}\n\n/**\n * Builds a clean, human-readable semantic selector for a DOM element.\n * Prioritizes data-testid/data-test, id, semantic tag + specific class, or concise parent path.\n */\nexport function getSemanticSelector(element: HTMLElement | Element, options: SelectorOptions = {}): string {\n if (!element || !element.tagName) return 'unknown';\n\n const maxClasses = options.maxClasses ?? 2;\n\n // 1. Check for standard test attributes\n const testId =\n element.getAttribute('data-testid') ||\n element.getAttribute('data-test') ||\n element.getAttribute('data-cy') ||\n element.getAttribute('data-qa');\n\n if (testId) {\n return `[data-testid=\"${testId}\"]`;\n }\n\n // 2. Check for meaningful ID (skip dynamic UUID/numeric IDs)\n const id = element.id;\n if (id && !/^[0-9]+$/.test(id) && !/[0-9a-f]{8}-[0-9a-f]{4}/i.test(id)) {\n return `#${id}`;\n }\n\n // 3. Check for aria-label or name on buttons/inputs\n const ariaLabel = element.getAttribute('aria-label');\n if (ariaLabel && ariaLabel.length < 30) {\n return `${element.tagName.toLowerCase()}[aria-label=\"${ariaLabel}\"]`;\n }\n\n const nameAttr = element.getAttribute('name');\n if (nameAttr) {\n return `${element.tagName.toLowerCase()}[name=\"${nameAttr}\"]`;\n }\n\n // 4. Tag + curated classes\n const tag = element.tagName.toLowerCase();\n const classList = Array.from(element.classList || [])\n .filter((c) => !c.startsWith('css-') && !c.startsWith('_') && !/^[a-z0-9]{5,}$/i.test(c)) // filter hashed utility classes\n .slice(0, maxClasses);\n\n if (classList.length > 0) {\n const classStr = classList.map((c) => `.${c}`).join('');\n return `${tag}${classStr}`;\n }\n\n // 5. If generic element (div, span, p) without attributes, try parent context\n if (element.parentElement && element.parentElement !== document.body && element.parentElement !== document.documentElement) {\n const parentSelector = getSemanticSelector(element.parentElement, { maxClasses: 1 });\n if (parentSelector && parentSelector !== 'unknown' && !parentSelector.includes('>')) {\n return `${parentSelector} > ${tag}`;\n }\n }\n\n return tag;\n}\n\n/**\n * Truncates string to specified max length with ellipsis\n */\nexport function truncate(str: string | undefined | null, maxLength = 200): string {\n if (!str) return '';\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n}\n"],"mappings":"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","names":[]}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
StorageDB,
|
|
7
7
|
VerificationEngine,
|
|
8
8
|
getDaemonConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ILRYKMME.js";
|
|
10
10
|
|
|
11
11
|
// packages/mcp/src/tools.ts
|
|
12
12
|
var TOOLS = [
|
|
@@ -656,4 +656,4 @@ export {
|
|
|
656
656
|
handleToolCall,
|
|
657
657
|
createMcpServer
|
|
658
658
|
};
|
|
659
|
-
//# sourceMappingURL=chunk-
|
|
659
|
+
//# sourceMappingURL=chunk-G2Y3CXCY.js.map
|
|
@@ -498,11 +498,19 @@ var StorageDB = class {
|
|
|
498
498
|
sql += " AND status = ?";
|
|
499
499
|
params.push(options.status);
|
|
500
500
|
}
|
|
501
|
+
if (options.route) {
|
|
502
|
+
sql += " AND route = ?";
|
|
503
|
+
params.push(options.route);
|
|
504
|
+
}
|
|
501
505
|
sql += " ORDER BY created_at DESC LIMIT ?";
|
|
502
506
|
params.push(options.limit || 50);
|
|
503
507
|
const rows = this.db.prepare(sql).all(...params);
|
|
504
508
|
return rows.map((r) => this.mapNoteRow(r));
|
|
505
509
|
}
|
|
510
|
+
deleteNote(id) {
|
|
511
|
+
this.db.prepare("DELETE FROM note_verifications WHERE note_id = ?").run(id);
|
|
512
|
+
this.db.prepare("DELETE FROM notes WHERE id = ?").run(id);
|
|
513
|
+
}
|
|
506
514
|
updateNoteStatus(id, status) {
|
|
507
515
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
508
516
|
const resolvedAt = status === "RESOLVED" ? now : null;
|
|
@@ -762,6 +770,27 @@ var SessionManager = class {
|
|
|
762
770
|
getActiveCount() {
|
|
763
771
|
return this.activeSockets.size;
|
|
764
772
|
}
|
|
773
|
+
broadcastToProject(projectId, message) {
|
|
774
|
+
const raw = typeof message === "string" ? message : JSON.stringify(message);
|
|
775
|
+
for (const active of this.activeSockets.values()) {
|
|
776
|
+
if (active.projectId === projectId && active.ws.readyState === 1) {
|
|
777
|
+
try {
|
|
778
|
+
active.ws.send(raw);
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
sendToSession(sessionId, message) {
|
|
785
|
+
const active = this.activeSockets.get(sessionId);
|
|
786
|
+
if (active && active.ws.readyState === 1) {
|
|
787
|
+
try {
|
|
788
|
+
const raw = typeof message === "string" ? message : JSON.stringify(message);
|
|
789
|
+
active.ws.send(raw);
|
|
790
|
+
} catch {
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
765
794
|
};
|
|
766
795
|
|
|
767
796
|
// packages/daemon/src/notes/engine.ts
|
|
@@ -1196,4 +1225,4 @@ export {
|
|
|
1196
1225
|
NoteVerificationEngine,
|
|
1197
1226
|
VerificationEngine
|
|
1198
1227
|
};
|
|
1199
|
-
//# sourceMappingURL=chunk-
|
|
1228
|
+
//# sourceMappingURL=chunk-ILRYKMME.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/daemon/src/config.ts","../packages/daemon/src/storage/db.ts","../packages/daemon/src/storage/screenshot-store.ts","../packages/daemon/src/session/manager.ts","../packages/daemon/src/notes/engine.ts","../packages/daemon/src/notes/verification.ts","../packages/daemon/src/verification/engine.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\nexport interface DaemonConfig {\n host: string;\n port: number;\n dataDir: string;\n dbPath: string;\n screenshotsDir: string;\n maxEventsPerSession: number;\n verbose: boolean;\n}\n\nexport function getDaemonConfig(overrides: Partial<DaemonConfig> = {}): DaemonConfig {\n const homeDir = os.homedir();\n const dataDir = overrides.dataDir || path.join(homeDir, '.browsertrack');\n const dbPath = overrides.dbPath || path.join(dataDir, 'browsertrack.db');\n const screenshotsDir = overrides.screenshotsDir || path.join(dataDir, 'projects');\n\n return {\n host: overrides.host || '127.0.0.1',\n port: overrides.port || 7331,\n dataDir,\n dbPath,\n screenshotsDir,\n maxEventsPerSession: overrides.maxEventsPerSession || 1000,\n verbose: !!overrides.verbose,\n };\n}\n","import Database from 'better-sqlite3';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type {\n Breadcrumb,\n ElementSummary,\n Incident,\n IncidentOccurrence,\n IncidentSeverity,\n IncidentStatus,\n NetworkEvent,\n Project,\n Session,\n VerificationResult,\n VisualNote,\n NoteStatus,\n NoteVerificationResult,\n} from '../../../core/src/index.js';\n\nexport class StorageDB {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n const dir = path.dirname(dbPath);\n fs.mkdirSync(dir, { recursive: true });\n\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.initTables();\n }\n\n private initTables(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT UNIQUE,\n origin TEXT,\n path TEXT,\n created_at TEXT,\n updated_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n origin TEXT,\n url TEXT,\n title TEXT,\n user_agent TEXT,\n connected_at TEXT,\n last_seen_at TEXT,\n active INTEGER DEFAULT 1\n );\n\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n session_id TEXT,\n event_type TEXT,\n payload TEXT,\n timestamp INTEGER,\n route TEXT,\n url TEXT\n );\n\n CREATE TABLE IF NOT EXISTS incidents (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n session_id TEXT,\n type TEXT,\n severity TEXT,\n message TEXT,\n source_file TEXT,\n source_line INTEGER,\n source_col INTEGER,\n fingerprint TEXT UNIQUE,\n route TEXT,\n first_seen TEXT,\n last_seen TEXT,\n occurrences INTEGER DEFAULT 1,\n status TEXT DEFAULT 'OPEN',\n stack TEXT,\n breadcrumbs TEXT,\n network_failures TEXT,\n last_element TEXT,\n screenshot_path TEXT\n );\n\n CREATE TABLE IF NOT EXISTS incident_occurrences (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n session_id TEXT,\n timestamp TEXT,\n route TEXT,\n url TEXT,\n stack TEXT,\n breadcrumbs TEXT,\n last_element TEXT\n );\n\n CREATE TABLE IF NOT EXISTS screenshots (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n file_path TEXT,\n format TEXT,\n width INTEGER,\n height INTEGER,\n created_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS verifications (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n status TEXT,\n checks TEXT,\n before_screenshot TEXT,\n after_screenshot TEXT,\n message TEXT,\n created_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS notes (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n session_id TEXT,\n type TEXT,\n message TEXT,\n route TEXT,\n url TEXT,\n viewport_json TEXT,\n scroll_json TEXT,\n target_json TEXT,\n element_context_json TEXT,\n region_json TEXT,\n screenshot_path TEXT,\n incident_id TEXT,\n status TEXT DEFAULT 'OPEN',\n created_at TEXT,\n updated_at TEXT,\n resolved_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS note_verifications (\n id TEXT PRIMARY KEY,\n note_id TEXT,\n status TEXT,\n checks TEXT,\n geometry_diff TEXT,\n before_screenshot TEXT,\n after_screenshot TEXT,\n message TEXT,\n created_at TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, timestamp);\n CREATE INDEX IF NOT EXISTS idx_incidents_project ON incidents(project_id, status);\n CREATE INDEX IF NOT EXISTS idx_incidents_fp ON incidents(fingerprint);\n CREATE INDEX IF NOT EXISTS idx_notes_project ON notes(project_id, status);\n `);\n }\n\n // --- PROJECTS ---\n public upsertProject(project: { id: string; name: string; origin: string; path?: string }): Project {\n const now = new Date().toISOString();\n const existing = this.db.prepare('SELECT * FROM projects WHERE name = ? OR origin = ?').get(project.name, project.origin) as any;\n\n if (existing) {\n this.db\n .prepare('UPDATE projects SET name = ?, origin = ?, path = COALESCE(?, path), updated_at = ? WHERE id = ?')\n .run(project.name, project.origin, project.path || null, now, existing.id);\n return {\n id: existing.id,\n name: project.name,\n origin: project.origin,\n path: project.path || existing.path,\n createdAt: existing.created_at,\n updatedAt: now,\n };\n }\n\n this.db\n .prepare('INSERT INTO projects (id, name, origin, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')\n .run(project.id, project.name, project.origin, project.path || null, now, now);\n\n return {\n id: project.id,\n name: project.name,\n origin: project.origin,\n path: project.path,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n public getProject(idOrName: string): Project | null {\n const row = this.db.prepare('SELECT * FROM projects WHERE id = ? OR name = ?').get(idOrName, idOrName) as any;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n public getProjectByOrigin(origin: string): Project | null {\n const row = this.db.prepare('SELECT * FROM projects WHERE origin = ?').get(origin) as any;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n public listProjects(): Project[] {\n const rows = this.db.prepare('SELECT * FROM projects ORDER BY updated_at DESC').all() as any[];\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }));\n }\n\n // --- SESSIONS ---\n public upsertSession(session: Session): void {\n const existing = this.db.prepare('SELECT id FROM sessions WHERE id = ?').get(session.id);\n if (existing) {\n this.db\n .prepare('UPDATE sessions SET url = ?, title = ?, last_seen_at = ?, active = ? WHERE id = ?')\n .run(session.url, session.title, session.lastSeenAt, session.active ? 1 : 0, session.id);\n } else {\n this.db\n .prepare(\n 'INSERT INTO sessions (id, project_id, origin, url, title, user_agent, connected_at, last_seen_at, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'\n )\n .run(\n session.id,\n session.projectId,\n session.origin,\n session.url,\n session.title,\n session.userAgent,\n session.connectedAt,\n session.lastSeenAt,\n session.active ? 1 : 0\n );\n }\n }\n\n public getSession(id: string): Session | null {\n const row = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as any;\n if (!row) return null;\n return {\n id: row.id,\n projectId: row.project_id,\n origin: row.origin,\n url: row.url,\n title: row.title,\n userAgent: row.user_agent,\n connectedAt: row.connected_at,\n lastSeenAt: row.last_seen_at,\n active: row.active === 1,\n };\n }\n\n public listSessions(projectId?: string, activeOnly = false): Session[] {\n let sql = 'SELECT * FROM sessions WHERE 1=1';\n const params: any[] = [];\n\n if (projectId) {\n sql += ' AND project_id = ?';\n params.push(projectId);\n }\n if (activeOnly) {\n sql += ' AND active = 1';\n }\n sql += ' ORDER BY last_seen_at DESC';\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((row) => ({\n id: row.id,\n projectId: row.project_id,\n origin: row.origin,\n url: row.url,\n title: row.title,\n userAgent: row.user_agent,\n connectedAt: row.connected_at,\n lastSeenAt: row.last_seen_at,\n active: row.active === 1,\n }));\n }\n\n public deactivateSession(id: string): void {\n this.db.prepare('UPDATE sessions SET active = 0, last_seen_at = ? WHERE id = ?').run(new Date().toISOString(), id);\n }\n\n // --- EVENTS & RETENTION ---\n public insertEvent(event: {\n id: string;\n sessionId: string;\n eventType: string;\n payload: any;\n timestamp: number;\n route?: string;\n url?: string;\n }): void {\n this.db\n .prepare('INSERT INTO events (id, session_id, event_type, payload, timestamp, route, url) VALUES (?, ?, ?, ?, ?, ?, ?)')\n .run(\n event.id,\n event.sessionId,\n event.eventType,\n JSON.stringify(event.payload),\n event.timestamp,\n event.route || '',\n event.url || ''\n );\n }\n\n public pruneSessionEvents(sessionId: string, maxEvents = 1000): void {\n this.db\n .prepare(\n `DELETE FROM events \n WHERE session_id = ? AND id NOT IN (\n SELECT id FROM events WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?\n )`\n )\n .run(sessionId, sessionId, maxEvents);\n }\n\n public getEvents(options: { sessionId?: string; eventType?: string; limit?: number }): any[] {\n let sql = 'SELECT * FROM events WHERE 1=1';\n const params: any[] = [];\n\n if (options.sessionId) {\n sql += ' AND session_id = ?';\n params.push(options.sessionId);\n }\n if (options.eventType) {\n sql += ' AND event_type = ?';\n params.push(options.eventType);\n }\n\n sql += ' ORDER BY timestamp DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n eventType: r.event_type,\n payload: JSON.parse(r.payload),\n timestamp: r.timestamp,\n route: r.route,\n url: r.url,\n }));\n }\n\n // --- INCIDENTS ---\n public findIncidentByFingerprint(fingerprint: string): Incident | null {\n const row = this.db.prepare('SELECT * FROM incidents WHERE fingerprint = ?').get(fingerprint) as any;\n if (!row) return null;\n return this.mapIncidentRow(row);\n }\n\n public getIncident(id: string): Incident | null {\n const row = this.db.prepare('SELECT * FROM incidents WHERE id = ?').get(id) as any;\n if (!row) return null;\n return this.mapIncidentRow(row);\n }\n\n public listIncidents(options: { projectId?: string; status?: IncidentStatus; severity?: IncidentSeverity; limit?: number } = {}): Incident[] {\n let sql = 'SELECT * FROM incidents WHERE 1=1';\n const params: any[] = [];\n\n if (options.projectId) {\n sql += ' AND project_id = ?';\n params.push(options.projectId);\n }\n if (options.status) {\n sql += ' AND status = ?';\n params.push(options.status);\n }\n if (options.severity) {\n sql += ' AND severity = ?';\n params.push(options.severity);\n }\n\n sql += ' ORDER BY last_seen DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => this.mapIncidentRow(r));\n }\n\n public insertIncident(incident: Incident): void {\n this.db\n .prepare(\n `INSERT INTO incidents (\n id, project_id, session_id, type, severity, message, source_file, source_line, source_col,\n fingerprint, route, first_seen, last_seen, occurrences, status, stack, breadcrumbs,\n network_failures, last_element, screenshot_path\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n incident.id,\n incident.projectId,\n incident.sessionId,\n incident.type,\n incident.severity,\n incident.message,\n incident.source.file,\n incident.source.line,\n incident.source.column || null,\n incident.fingerprint,\n incident.route,\n incident.firstSeen,\n incident.lastSeen,\n incident.occurrences,\n incident.status,\n incident.stack || null,\n JSON.stringify(incident.breadcrumbs || []),\n JSON.stringify(incident.networkFailures || []),\n incident.lastElement ? JSON.stringify(incident.lastElement) : null,\n incident.screenshots?.error || null\n );\n }\n\n public updateIncidentOccurrence(\n incidentId: string,\n update: {\n sessionId: string;\n lastSeen: string;\n occurrences: number;\n route: string;\n breadcrumbs: Breadcrumb[];\n lastElement?: ElementSummary;\n stack?: string;\n }\n ): void {\n this.db\n .prepare(\n `UPDATE incidents \n SET session_id = ?, last_seen = ?, occurrences = ?, route = ?, breadcrumbs = ?, last_element = COALESCE(?, last_element), stack = COALESCE(?, stack)\n WHERE id = ?`\n )\n .run(\n update.sessionId,\n update.lastSeen,\n update.occurrences,\n update.route,\n JSON.stringify(update.breadcrumbs || []),\n update.lastElement ? JSON.stringify(update.lastElement) : null,\n update.stack || null,\n incidentId\n );\n }\n\n public updateIncidentStatus(id: string, status: IncidentStatus): void {\n this.db.prepare('UPDATE incidents SET status = ? WHERE id = ?').run(status, id);\n }\n\n public insertIncidentOccurrence(occurrence: IncidentOccurrence): void {\n this.db\n .prepare(\n `INSERT INTO incident_occurrences (id, incident_id, session_id, timestamp, route, url, stack, breadcrumbs, last_element)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n occurrence.id,\n occurrence.incidentId,\n occurrence.sessionId,\n occurrence.timestamp,\n occurrence.route,\n occurrence.url,\n occurrence.stack || null,\n JSON.stringify(occurrence.breadcrumbs || []),\n occurrence.lastElement ? JSON.stringify(occurrence.lastElement) : null\n );\n }\n\n // --- VERIFICATIONS ---\n public insertVerification(v: {\n id: string;\n incidentId: string;\n status: IncidentStatus;\n checks: any[];\n beforeScreenshot?: string;\n afterScreenshot?: string;\n message?: string;\n createdAt: string;\n }): void {\n this.db\n .prepare(\n `INSERT INTO verifications (id, incident_id, status, checks, before_screenshot, after_screenshot, message, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n v.id,\n v.incidentId,\n v.status,\n JSON.stringify(v.checks),\n v.beforeScreenshot || null,\n v.afterScreenshot || null,\n v.message || null,\n v.createdAt\n );\n }\n\n public getLatestVerification(incidentId: string): VerificationResult | null {\n const row = this.db.prepare('SELECT * FROM verifications WHERE incident_id = ? ORDER BY created_at DESC LIMIT 1').get(incidentId) as any;\n if (!row) return null;\n return {\n incidentId: row.incident_id,\n status: row.status as IncidentStatus,\n checks: JSON.parse(row.checks || '[]'),\n screenshots: {\n before: row.before_screenshot || undefined,\n after: row.after_screenshot || undefined,\n },\n timestamp: row.created_at,\n message: row.message || undefined,\n };\n }\n\n // --- VISUAL NOTES ---\n public insertNote(note: VisualNote): void {\n this.db\n .prepare(\n `INSERT INTO notes (\n id, project_id, session_id, type, message, route, url,\n viewport_json, scroll_json, target_json, element_context_json, region_json,\n screenshot_path, incident_id, status, created_at, updated_at, resolved_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n note.id,\n note.projectId,\n note.sessionId,\n note.type,\n note.message,\n note.route,\n note.url,\n JSON.stringify(note.viewport),\n JSON.stringify(note.scroll),\n note.target ? JSON.stringify(note.target) : null,\n note.elementContext ? JSON.stringify(note.elementContext) : null,\n note.region ? JSON.stringify(note.region) : null,\n note.screenshots?.original || null,\n note.incidentId || null,\n note.status,\n note.createdAt,\n note.updatedAt,\n note.resolvedAt || null\n );\n }\n\n public getNote(id: string): VisualNote | null {\n const row = this.db.prepare('SELECT * FROM notes WHERE id = ?').get(id) as any;\n if (!row) return null;\n return this.mapNoteRow(row);\n }\n\n public listNotes(options: { projectId?: string; status?: NoteStatus; route?: string; limit?: number } = {}): VisualNote[] {\n let sql = 'SELECT * FROM notes WHERE 1=1';\n const params: any[] = [];\n\n if (options.projectId) {\n sql += ' AND project_id = ?';\n params.push(options.projectId);\n }\n if (options.status) {\n sql += ' AND status = ?';\n params.push(options.status);\n }\n if (options.route) {\n sql += ' AND route = ?';\n params.push(options.route);\n }\n\n sql += ' ORDER BY created_at DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => this.mapNoteRow(r));\n }\n\n public deleteNote(id: string): void {\n this.db.prepare('DELETE FROM note_verifications WHERE note_id = ?').run(id);\n this.db.prepare('DELETE FROM notes WHERE id = ?').run(id);\n }\n\n public updateNoteStatus(id: string, status: NoteStatus): void {\n const now = new Date().toISOString();\n const resolvedAt = status === 'RESOLVED' ? now : null;\n this.db\n .prepare('UPDATE notes SET status = ?, updated_at = ?, resolved_at = COALESCE(?, resolved_at) WHERE id = ?')\n .run(status, now, resolvedAt, id);\n }\n\n public updateNote(id: string, updates: Partial<VisualNote>): void {\n const now = new Date().toISOString();\n const current = this.getNote(id);\n if (!current) return;\n\n this.db\n .prepare(\n `UPDATE notes \n SET message = COALESCE(?, message), status = COALESCE(?, status),\n screenshot_path = COALESCE(?, screenshot_path), updated_at = ?\n WHERE id = ?`\n )\n .run(\n updates.message || null,\n updates.status || null,\n updates.screenshots?.original || updates.screenshots?.after || null,\n now,\n id\n );\n }\n\n public insertNoteVerification(v: NoteVerificationResult): void {\n this.db\n .prepare(\n `INSERT INTO note_verifications (\n id, note_id, status, checks, geometry_diff, before_screenshot, after_screenshot, message, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n `nver_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n v.noteId,\n v.status,\n JSON.stringify(v.checks),\n v.geometryDiff ? JSON.stringify(v.geometryDiff) : null,\n v.screenshots?.before || null,\n v.screenshots?.after || null,\n v.message || null,\n v.timestamp\n );\n }\n\n public getLatestNoteVerification(noteId: string): NoteVerificationResult | null {\n const row = this.db.prepare('SELECT * FROM note_verifications WHERE note_id = ? ORDER BY created_at DESC LIMIT 1').get(noteId) as any;\n if (!row) return null;\n return {\n noteId: row.note_id,\n status: row.status as NoteStatus,\n checks: JSON.parse(row.checks || '[]'),\n geometryDiff: row.geometry_diff ? JSON.parse(row.geometry_diff) : undefined,\n screenshots: {\n before: row.before_screenshot || undefined,\n after: row.after_screenshot || undefined,\n },\n timestamp: row.created_at,\n message: row.message || undefined,\n };\n }\n\n public clearAll(): void {\n this.db.exec(`\n DELETE FROM events;\n DELETE FROM incident_occurrences;\n DELETE FROM incidents;\n DELETE FROM screenshots;\n DELETE FROM verifications;\n DELETE FROM notes;\n DELETE FROM note_verifications;\n DELETE FROM sessions;\n `);\n }\n\n private mapIncidentRow(row: any): Incident {\n return {\n id: row.id,\n projectId: row.project_id,\n sessionId: row.session_id,\n type: row.type,\n severity: row.severity as IncidentSeverity,\n message: row.message,\n source: {\n file: row.source_file,\n line: row.source_line,\n column: row.source_col || undefined,\n },\n fingerprint: row.fingerprint,\n route: row.route,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n occurrences: row.occurrences,\n status: row.status as IncidentStatus,\n stack: row.stack || undefined,\n breadcrumbs: JSON.parse(row.breadcrumbs || '[]'),\n networkFailures: JSON.parse(row.network_failures || '[]'),\n lastElement: row.last_element ? JSON.parse(row.last_element) : undefined,\n screenshots: row.screenshot_path\n ? {\n error: row.screenshot_path,\n }\n : undefined,\n };\n }\n\n private mapNoteRow(row: any): VisualNote {\n return {\n id: row.id,\n projectId: row.project_id,\n sessionId: row.session_id,\n type: row.type,\n message: row.message,\n route: row.route,\n url: row.url,\n viewport: JSON.parse(row.viewport_json || '{}'),\n scroll: JSON.parse(row.scroll_json || '{}'),\n target: row.target_json ? JSON.parse(row.target_json) : undefined,\n elementContext: row.element_context_json ? JSON.parse(row.element_context_json) : undefined,\n region: row.region_json ? JSON.parse(row.region_json) : undefined,\n status: row.status as NoteStatus,\n incidentId: row.incident_id || undefined,\n screenshots: row.screenshot_path\n ? {\n original: row.screenshot_path,\n }\n : undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n resolvedAt: row.resolved_at || undefined,\n };\n }\n\n public close(): void {\n this.db.close();\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport class ScreenshotStore {\n private baseDir: string;\n\n constructor(baseDir: string) {\n this.baseDir = baseDir;\n fs.mkdirSync(this.baseDir, { recursive: true });\n }\n\n public saveScreenshot(projectId: string, incidentId: string, name: string, dataUrl: string): { filePath: string; format: string } | null {\n try {\n if (!dataUrl || !dataUrl.startsWith('data:image/')) return null;\n\n const match = dataUrl.match(/^data:image\\/([a-zA-Z0-9+]+);base64,(.+)$/);\n if (!match) return null;\n\n let format = match[1].toLowerCase();\n if (format === 'jpeg') format = 'jpg';\n const base64Data = match[2];\n const buffer = Buffer.from(base64Data, 'base64');\n\n const targetDir = path.join(this.baseDir, projectId || 'default', 'incidents', incidentId);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const fileName = `${name}.${format}`;\n const filePath = path.join(targetDir, fileName);\n\n fs.writeFileSync(filePath, buffer);\n\n return { filePath, format };\n } catch {\n return null;\n }\n }\n\n public getScreenshotPath(projectId: string, incidentId: string, name: string): string | null {\n const targetDir = path.join(this.baseDir, projectId || 'default', 'incidents', incidentId);\n if (!fs.existsSync(targetDir)) return null;\n\n const files = fs.readdirSync(targetDir);\n const match = files.find((f) => f.startsWith(`${name}.`));\n if (match) {\n return path.join(targetDir, match);\n }\n return null;\n }\n}\n","import type { WebSocket } from 'ws';\nimport type { ClientCommand, CommandResponse, Session } from '../../../core/src/index.js';\nimport type { StorageDB } from '../storage/db.js';\n\ninterface ActiveSessionSocket {\n sessionId: string;\n ws: WebSocket;\n origin: string;\n projectId: string;\n pendingCommands: Map<\n string,\n {\n resolve: (res: CommandResponse) => void;\n reject: (err: Error) => void;\n timer: any;\n }\n >;\n}\n\nexport class SessionManager {\n private activeSockets: Map<string, ActiveSessionSocket> = new Map();\n private db: StorageDB;\n\n constructor(db: StorageDB) {\n this.db = db;\n }\n\n public registerSocket(sessionId: string, ws: WebSocket, origin: string, projectId: string): void {\n this.activeSockets.set(sessionId, {\n sessionId,\n ws,\n origin,\n projectId,\n pendingCommands: new Map(),\n });\n }\n\n public unregisterSocket(sessionId: string): void {\n const active = this.activeSockets.get(sessionId);\n if (active) {\n for (const pending of active.pendingCommands.values()) {\n clearTimeout(pending.timer);\n pending.reject(new Error('Session disconnected before command completed'));\n }\n active.pendingCommands.clear();\n this.activeSockets.delete(sessionId);\n this.db.deactivateSession(sessionId);\n }\n }\n\n public handleCommandResponse(sessionId: string, response: CommandResponse): void {\n const active = this.activeSockets.get(sessionId);\n if (!active) return;\n\n const pending = active.pendingCommands.get(response.id);\n if (pending) {\n clearTimeout(pending.timer);\n active.pendingCommands.delete(response.id);\n pending.resolve(response);\n }\n }\n\n public async sendCommand<T = any>(sessionId: string, command: ClientCommand<T>, timeoutMs = 5000): Promise<CommandResponse> {\n const active = this.activeSockets.get(sessionId);\n if (!active || active.ws.readyState !== 1 /* WebSocket.OPEN */) {\n return {\n id: command.id,\n ok: false,\n error: `Session ${sessionId} is not actively connected`,\n };\n }\n\n return new Promise<CommandResponse>((resolve, reject) => {\n const timer = setTimeout(() => {\n active.pendingCommands.delete(command.id);\n resolve({\n id: command.id,\n ok: false,\n error: `Command ${command.type} timed out after ${timeoutMs}ms`,\n });\n }, timeoutMs);\n\n active.pendingCommands.set(command.id, {\n resolve,\n reject,\n timer,\n });\n\n try {\n active.ws.send(\n JSON.stringify({\n type: 'command',\n command,\n })\n );\n } catch (err: any) {\n clearTimeout(timer);\n active.pendingCommands.delete(command.id);\n resolve({\n id: command.id,\n ok: false,\n error: err?.message || 'Failed to send command over WebSocket',\n });\n }\n });\n }\n\n public getActiveSessionForProject(projectId: string): Session | null {\n for (const active of this.activeSockets.values()) {\n if (active.projectId === projectId) {\n return this.db.getSession(active.sessionId);\n }\n }\n return null;\n }\n\n public getAnyActiveSession(): Session | null {\n const first = this.activeSockets.keys().next().value;\n if (!first) return null;\n return this.db.getSession(first);\n }\n\n public getActiveCount(): number {\n return this.activeSockets.size;\n }\n\n public broadcastToProject(projectId: string, message: any): void {\n const raw = typeof message === 'string' ? message : JSON.stringify(message);\n for (const active of this.activeSockets.values()) {\n if (active.projectId === projectId && active.ws.readyState === 1 /* OPEN */) {\n try {\n active.ws.send(raw);\n } catch {}\n }\n }\n }\n\n public sendToSession(sessionId: string, message: any): void {\n const active = this.activeSockets.get(sessionId);\n if (active && active.ws.readyState === 1) {\n try {\n const raw = typeof message === 'string' ? message : JSON.stringify(message);\n active.ws.send(raw);\n } catch {}\n }\n }\n}\n","import crypto from 'node:crypto';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { VisualNote } from '../../../core/src/index.js';\nimport type { StorageDB } from '../storage/db.js';\nimport type { ScreenshotStore } from '../storage/screenshot-store.js';\n\nexport class NotesEngine {\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 createNoteFromClient(payload: {\n sessionId: string;\n noteType?: 'element' | 'region' | 'page';\n message: string;\n route?: string;\n url?: string;\n viewport?: { width: number; height: number; devicePixelRatio: number };\n scroll?: { scrollX: number; scrollY: number };\n target?: any;\n elementContext?: any;\n region?: any;\n screenshot?: string;\n incidentId?: string;\n }): VisualNote {\n const session = this.db.getSession(payload.sessionId);\n const projectId = session?.projectId || 'default';\n const noteId = `note_${crypto.randomUUID().slice(0, 8)}`;\n const now = new Date().toISOString();\n\n let screenshotPath: string | undefined;\n if (payload.screenshot) {\n const saved = this.saveNoteScreenshot(projectId, noteId, 'original', payload.screenshot);\n if (saved) {\n screenshotPath = saved;\n }\n }\n\n const note: VisualNote = {\n id: noteId,\n projectId,\n sessionId: payload.sessionId,\n type: payload.noteType || 'element',\n message: payload.message,\n route: payload.route || '/',\n url: payload.url || '',\n viewport: payload.viewport || { width: 1280, height: 800, devicePixelRatio: 1 },\n scroll: payload.scroll || { scrollX: 0, scrollY: 0 },\n target: payload.target,\n elementContext: payload.elementContext,\n region: payload.region,\n status: 'OPEN',\n incidentId: payload.incidentId,\n screenshots: screenshotPath ? { original: screenshotPath } : undefined,\n createdAt: now,\n updatedAt: now,\n };\n\n this.db.insertNote(note);\n return note;\n }\n\n public saveNoteScreenshot(projectId: string, noteId: string, name: string, dataUrl: string): string | null {\n try {\n if (!dataUrl || !dataUrl.startsWith('data:image/')) return null;\n\n const match = dataUrl.match(/^data:image\\/([a-zA-Z0-9+]+);base64,(.+)$/);\n if (!match) return null;\n\n let format = match[1].toLowerCase();\n if (format === 'jpeg') format = 'jpg';\n const base64Data = match[2];\n const buffer = Buffer.from(base64Data, 'base64');\n\n const targetDir = path.join((this.screenshotStore as any).baseDir, projectId || 'default', 'notes', noteId);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const fileName = `${name}.${format}`;\n const filePath = path.join(targetDir, fileName);\n\n fs.writeFileSync(filePath, buffer);\n return filePath;\n } catch {\n return null;\n }\n }\n}\n","import crypto from 'node:crypto';\nimport type { DOMRectJson, NoteStatus, NoteVerificationResult, OverflowCheckResult, VisualNote } from '../../../core/src/index.js';\nimport type { NotesEngine } from './engine.js';\nimport type { SessionManager } from '../session/manager.js';\nimport type { StorageDB } from '../storage/db.js';\n\nexport class NoteVerificationEngine {\n private db: StorageDB;\n private sessionManager: SessionManager;\n private notesEngine: NotesEngine;\n\n constructor(db: StorageDB, sessionManager: SessionManager, notesEngine: NotesEngine) {\n this.db = db;\n this.sessionManager = sessionManager;\n this.notesEngine = notesEngine;\n }\n\n public async verifyNote(noteId: string, options: { observationWindowMs?: number } = {}): Promise<NoteVerificationResult> {\n const note = this.db.getNote(noteId);\n if (!note) {\n return {\n noteId,\n status: 'FAILED',\n checks: [{ type: 'note_exists', passed: false, details: `Note ${noteId} not found` }],\n timestamp: new Date().toISOString(),\n message: `Note ${noteId} not found in database`,\n };\n }\n\n // 1. Find active session\n let session = this.sessionManager.getActiveSessionForProject(note.projectId);\n if (!session) {\n session = this.sessionManager.getAnyActiveSession();\n }\n\n if (!session) {\n const result: NoteVerificationResult = {\n noteId,\n status: 'INCONCLUSIVE',\n checks: [],\n timestamp: new Date().toISOString(),\n message: 'No active browser session currently connected to verify visual note.',\n };\n this.recordNoteVerification(result, note);\n return result;\n }\n\n // 2. Set status to VERIFYING\n this.db.updateNoteStatus(noteId, 'VERIFYING');\n\n // 3. Navigation check / route sync\n const pageStateCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'get_page_state',\n });\n\n const currentRoute = pageStateCmd.result?.route || '/';\n const isRouteMatch = currentRoute === note.route || currentRoute.startsWith(note.route);\n\n if (!isRouteMatch && note.route) {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'navigate',\n params: { url: note.route },\n });\n // Wait for navigation observation window\n await new Promise((r) => setTimeout(r, options.observationWindowMs || 1000));\n }\n\n const checks: Array<{ type: string; passed: boolean; details?: string }> = [];\n\n checks.push({\n type: 'route_loaded',\n passed: true,\n details: `Route ${note.route || '/'} loaded`,\n });\n\n // 4. Target element checks\n const targetSelector = note.target?.selector || (note.type === 'page' ? 'body' : 'body');\n let elementExists = true;\n let elementVisible = true;\n let currentRect: DOMRectJson | undefined;\n let overflowResult: OverflowCheckResult | undefined;\n\n if (note.type === 'element' && targetSelector) {\n const queryCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: targetSelector },\n });\n\n elementExists = queryCmd.ok && !!queryCmd.result?.exists;\n elementVisible = queryCmd.ok && !!queryCmd.result?.visible;\n currentRect = queryCmd.result?.boundingRect;\n\n checks.push({\n type: 'element_exists',\n passed: elementExists,\n details: elementExists ? `Element ${targetSelector} exists in DOM` : `Element ${targetSelector} not found`,\n });\n\n checks.push({\n type: 'element_visible',\n passed: elementVisible,\n details: elementVisible ? `Element ${targetSelector} is visible` : `Element ${targetSelector} is hidden`,\n });\n\n // 5. Layout Overflow Probe\n const overflowCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'check_overflow',\n params: { selector: targetSelector },\n });\n\n if (overflowCmd.ok && overflowCmd.result) {\n const ovf = overflowCmd.result as OverflowCheckResult;\n overflowResult = ovf;\n const isOverflowingNow = !!ovf.overflow;\n\n checks.push({\n type: 'no_viewport_overflow',\n passed: !isOverflowingNow,\n details: !isOverflowingNow\n ? `Element fits within viewport (width: ${ovf.viewportWidth}px, rect right: ${ovf.rect?.right}px)`\n : `Element overflows viewport by ${ovf.overflowRightPx}px (rect right: ${ovf.rect?.right}px, viewport: ${ovf.viewportWidth}px)`,\n });\n }\n }\n\n // 6. Capture \"after\" screenshot\n let afterScreenshotPath: string | undefined;\n const captureCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'capture_element',\n params: { selector: targetSelector },\n });\n\n if (captureCmd.ok && captureCmd.result?.dataUrl) {\n const saved = this.notesEngine.saveNoteScreenshot(note.projectId, note.id, 'after', captureCmd.result.dataUrl);\n if (saved) {\n afterScreenshotPath = saved;\n }\n }\n\n // 7. Calculate Geometry Difference\n const beforeRect = note.target?.boundingRect;\n const overflowFixed = overflowResult ? !overflowResult.overflow : undefined;\n\n const geometryDiff = {\n before: beforeRect,\n current: currentRect || (overflowResult?.rect as DOMRectJson),\n viewportWidth: overflowResult?.viewportWidth || note.viewport.width,\n overflowFixed,\n overflowPx: overflowResult?.overflowRightPx || 0,\n };\n\n // 8. Verdict\n const anyFailed = checks.some((c) => !c.passed);\n let status: NoteStatus = 'VERIFIED';\n\n if (anyFailed) {\n status = 'FAILED';\n } else {\n status = 'VERIFIED';\n }\n\n const verificationResult: NoteVerificationResult = {\n noteId,\n status,\n checks,\n geometryDiff,\n screenshots: {\n before: note.screenshots?.original,\n after: afterScreenshotPath,\n },\n timestamp: new Date().toISOString(),\n message:\n status === 'VERIFIED'\n ? 'Visual note verified: target element is present, visible, and layout checks (including viewport overflow) passed.'\n : 'Visual note verification failed: target element missing, hidden, or still overflowing.',\n };\n\n this.recordNoteVerification(verificationResult, note);\n return verificationResult;\n }\n\n private recordNoteVerification(result: NoteVerificationResult, note: VisualNote): void {\n this.db.updateNoteStatus(note.id, result.status);\n this.db.insertNoteVerification(result);\n }\n}\n","import crypto from 'node:crypto';\nimport type {\n Incident,\n IncidentStatus,\n ProbeResult,\n VerificationProbe,\n VerificationRecipe,\n VerificationResult,\n} from '../../../core/src/index.js';\nimport type { SessionManager } from '../session/manager.js';\nimport type { StorageDB } from '../storage/db.js';\nimport type { ScreenshotStore } from '../storage/screenshot-store.js';\n\nexport class VerificationEngine {\n private db: StorageDB;\n private sessionManager: SessionManager;\n private screenshotStore: ScreenshotStore;\n\n constructor(db: StorageDB, sessionManager: SessionManager, screenshotStore: ScreenshotStore) {\n this.db = db;\n this.sessionManager = sessionManager;\n this.screenshotStore = screenshotStore;\n }\n\n public async verifyIncident(incidentId: string, recipe?: VerificationRecipe): Promise<VerificationResult> {\n const incident = this.db.getIncident(incidentId);\n if (!incident) {\n return {\n incidentId,\n status: 'FAILED',\n checks: [{ type: 'no_incident', passed: false, details: `Incident ${incidentId} not found` }],\n timestamp: new Date().toISOString(),\n message: `Incident ${incidentId} not found in database`,\n };\n }\n\n // 1. Find active session\n let session = this.sessionManager.getActiveSessionForProject(incident.projectId);\n if (!session) {\n session = this.sessionManager.getAnyActiveSession();\n }\n\n if (!session) {\n const result: VerificationResult = {\n incidentId,\n status: 'INCONCLUSIVE',\n checks: [],\n timestamp: new Date().toISOString(),\n message: 'No active browser session currently connected to verify the fix.',\n };\n this.recordVerification(result, incident);\n return result;\n }\n\n // 2. Set status to VERIFYING and record baseline\n this.db.updateIncidentStatus(incidentId, 'VERIFYING');\n const baselineOccurrences = incident.occurrences;\n\n // 3. Initiate browser reload or navigation\n const targetRoute = recipe?.route || incident.route;\n const observationMs = recipe?.observationWindowMs || 2000;\n\n if (recipe?.route && recipe.route !== incident.route) {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'navigate',\n params: { url: recipe.route },\n });\n } else {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'reload',\n params: { force: true },\n });\n }\n\n // 4. Observation window\n await new Promise((resolve) => setTimeout(resolve, observationMs));\n\n // 5. Check if incident re-occurred\n const freshIncident = this.db.getIncident(incidentId);\n const hasReoccurred = freshIncident ? freshIncident.occurrences > baselineOccurrences : false;\n\n const checks: ProbeResult[] = [];\n checks.push({\n type: 'no_incident',\n passed: !hasReoccurred,\n details: hasReoccurred\n ? `Incident reoccurred (${freshIncident?.occurrences} occurrences vs baseline ${baselineOccurrences})`\n : 'No recurring error observed during verification window',\n });\n\n // 6. Run additional probes\n const probes = recipe?.expect || [];\n for (const probe of probes) {\n const probeRes = await this.evaluateProbe(session.id, probe);\n checks.push(probeRes);\n }\n\n // 7. Check target element or last element for after-screenshot\n let afterScreenshotPath: string | undefined;\n const targetSelector = recipe?.targetSelector || incident.lastElement?.selector;\n\n if (targetSelector) {\n const captureCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'capture_element',\n params: { selector: targetSelector },\n });\n\n if (captureCmd.ok && captureCmd.result?.dataUrl) {\n const saved = this.screenshotStore.saveScreenshot(\n incident.projectId,\n incident.id,\n 'verified',\n captureCmd.result.dataUrl\n );\n if (saved) {\n afterScreenshotPath = saved.filePath;\n }\n }\n }\n\n // 8. Calculate verdict\n let verdict: IncidentStatus = 'VERIFIED';\n const anyCheckFailed = checks.some((c) => !c.passed);\n\n if (anyCheckFailed || hasReoccurred) {\n verdict = 'FAILED';\n } else {\n // Check if incident required an explicit interaction (like button click) that wasn't covered by probes\n const wasInteractionError = incident.breadcrumbs.some((b) => b.type === 'click' || b.type === 'submit');\n if (wasInteractionError && probes.length === 0) {\n verdict = 'INCONCLUSIVE';\n }\n }\n\n const verificationResult: VerificationResult = {\n incidentId,\n status: verdict,\n checks,\n screenshots: {\n before: incident.screenshots?.error,\n after: afterScreenshotPath,\n },\n timestamp: new Date().toISOString(),\n message:\n verdict === 'VERIFIED'\n ? 'Fix verified: Error did not reoccur and all checks passed.'\n : verdict === 'FAILED'\n ? 'Verification failed: Error reoccurred or probe expectation was not met.'\n : 'Verification inconclusive: Error did not appear on reload, but specific interaction may need manual verification or custom probes.',\n };\n\n this.recordVerification(verificationResult, incident);\n return verificationResult;\n }\n\n private async evaluateProbe(sessionId: string, probe: VerificationProbe): Promise<ProbeResult> {\n try {\n switch (probe.type) {\n case 'element_exists': {\n if (!probe.selector) {\n return { type: probe.type, passed: false, details: 'Missing selector in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const exists = res.ok && !!res.result?.exists;\n return {\n type: probe.type,\n passed: exists,\n details: exists ? `Element ${probe.selector} exists in DOM` : `Element ${probe.selector} does not exist`,\n };\n }\n\n case 'element_visible': {\n if (!probe.selector) {\n return { type: probe.type, passed: false, details: 'Missing selector in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const visible = res.ok && !!res.result?.exists && !!res.result?.visible;\n return {\n type: probe.type,\n passed: visible,\n details: visible ? `Element ${probe.selector} is visible` : `Element ${probe.selector} is not visible`,\n };\n }\n\n case 'text_contains': {\n if (!probe.selector || !probe.text) {\n return { type: probe.type, passed: false, details: 'Missing selector or text in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const innerText = res.result?.innerText || '';\n const contains = res.ok && innerText.includes(probe.text);\n return {\n type: probe.type,\n passed: contains,\n details: contains\n ? `Element ${probe.selector} contains text \"${probe.text}\"`\n : `Element text \"${innerText}\" did not contain \"${probe.text}\"`,\n };\n }\n\n case 'route_is': {\n if (!probe.route) {\n return { type: probe.type, passed: false, details: 'Missing route in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'get_page_state',\n });\n const currentRoute = res.result?.route || '';\n const matches = res.ok && (currentRoute === probe.route || currentRoute.startsWith(probe.route));\n return {\n type: probe.type,\n passed: matches,\n details: matches\n ? `Current route matches ${probe.route}`\n : `Current route is \"${currentRoute}\", expected \"${probe.route}\"`,\n };\n }\n\n default:\n return {\n type: probe.type,\n passed: true,\n details: `Probe ${probe.type} evaluated`,\n };\n }\n } catch (err: any) {\n return {\n type: probe.type,\n passed: false,\n details: err?.message || 'Probe execution error',\n };\n }\n }\n\n private recordVerification(result: VerificationResult, incident: Incident): void {\n this.db.updateIncidentStatus(incident.id, result.status);\n this.db.insertVerification({\n id: `ver_${crypto.randomUUID().slice(0, 8)}`,\n incidentId: incident.id,\n status: result.status,\n checks: result.checks,\n beforeScreenshot: result.screenshots?.before,\n afterScreenshot: result.screenshots?.after,\n message: result.message,\n createdAt: result.timestamp,\n });\n }\n}\n"],"mappings":"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","names":["path","fs","path","path","fs","crypto","crypto"]}
|