@clueprint/mcp 1.1.0

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 (60) hide show
  1. package/README.md +113 -0
  2. package/assets/logo.png +0 -0
  3. package/cli/commands/setup.d.ts +7 -0
  4. package/cli/commands/setup.d.ts.map +1 -0
  5. package/cli/commands/setup.js +248 -0
  6. package/cli/commands/setup.js.map +1 -0
  7. package/cli/commands/start.d.ts +6 -0
  8. package/cli/commands/start.d.ts.map +1 -0
  9. package/cli/commands/start.js +115 -0
  10. package/cli/commands/start.js.map +1 -0
  11. package/cli/commands/status.d.ts +2 -0
  12. package/cli/commands/status.d.ts.map +1 -0
  13. package/cli/commands/status.js +186 -0
  14. package/cli/commands/status.js.map +1 -0
  15. package/cli/index.d.ts +3 -0
  16. package/cli/index.d.ts.map +1 -0
  17. package/cli/index.js +53 -0
  18. package/cli/index.js.map +1 -0
  19. package/cli/ui/banner.d.ts +8 -0
  20. package/cli/ui/banner.d.ts.map +1 -0
  21. package/cli/ui/banner.js +41 -0
  22. package/cli/ui/banner.js.map +1 -0
  23. package/extension/background/index.js +2 -0
  24. package/extension/content/index.js +986 -0
  25. package/extension/devtools/devtools.html +10 -0
  26. package/extension/devtools/devtools.js +1 -0
  27. package/extension/devtools/panel.css +1584 -0
  28. package/extension/devtools/panel.html +16 -0
  29. package/extension/devtools/panel.js +40 -0
  30. package/extension/icons/clueprint.png +0 -0
  31. package/extension/manifest.json +70 -0
  32. package/extension/popup/popup.css +1584 -0
  33. package/extension/popup/popup.html +16 -0
  34. package/extension/popup/popup.js +122 -0
  35. package/extension/styles/overlay.css +111 -0
  36. package/package.json +41 -0
  37. package/server/analysis/format.d.ts +21 -0
  38. package/server/analysis/format.d.ts.map +1 -0
  39. package/server/analysis/format.js +583 -0
  40. package/server/analysis/format.js.map +1 -0
  41. package/server/index.d.ts +7 -0
  42. package/server/index.d.ts.map +1 -0
  43. package/server/index.js +14 -0
  44. package/server/index.js.map +1 -0
  45. package/server/server.d.ts +8 -0
  46. package/server/server.d.ts.map +1 -0
  47. package/server/server.js +417 -0
  48. package/server/server.js.map +1 -0
  49. package/server/shared-state.d.ts +14 -0
  50. package/server/shared-state.d.ts.map +1 -0
  51. package/server/shared-state.js +131 -0
  52. package/server/shared-state.js.map +1 -0
  53. package/server/types/index.d.ts +239 -0
  54. package/server/types/index.d.ts.map +1 -0
  55. package/server/types/index.js +7 -0
  56. package/server/types/index.js.map +1 -0
  57. package/server/websocket.d.ts +55 -0
  58. package/server/websocket.d.ts.map +1 -0
  59. package/server/websocket.js +355 -0
  60. package/server/websocket.js.map +1 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * MCP Server Types
3
+ * Mirrors extension types for communication
4
+ */
5
+ export type Intent = 'fix' | 'beautify' | 'other';
6
+ export type EventType = 'refresh' | 'navigation' | 'click' | 'input' | 'scroll' | 'network_request' | 'network_response' | 'network_error' | 'console_log' | 'console_warn' | 'console_error' | 'dom_mutation' | 'layout_shift' | 'element_select' | 'form_submit' | 'keypress' | 'mouse_move' | 'clipboard' | 'selection' | 'focus' | 'blur';
7
+ export interface ElementRect {
8
+ width: number;
9
+ height: number;
10
+ top: number;
11
+ left: number;
12
+ }
13
+ export interface ElementStyles {
14
+ layout: Record<string, string>;
15
+ size: Record<string, string>;
16
+ spacing: Record<string, string>;
17
+ visual: Record<string, string>;
18
+ text?: Record<string, string>;
19
+ }
20
+ export interface ParentInfo {
21
+ selector: string;
22
+ tag: string;
23
+ styles: Record<string, string>;
24
+ }
25
+ export interface SiblingInfo {
26
+ selector: string;
27
+ size: {
28
+ width: number;
29
+ height: number;
30
+ };
31
+ classes: string[];
32
+ isSelected: boolean;
33
+ anomaly?: string;
34
+ }
35
+ export interface CSSRule {
36
+ source: string;
37
+ selector: string;
38
+ properties: Record<string, string>;
39
+ isOverriding?: boolean;
40
+ }
41
+ export interface ConsoleEntry {
42
+ type: 'log' | 'warn' | 'error';
43
+ message: string;
44
+ stack?: string;
45
+ source?: string;
46
+ timestamp: number;
47
+ count?: number;
48
+ }
49
+ export interface NetworkEntry {
50
+ url: string;
51
+ method: string;
52
+ status?: number;
53
+ statusText?: string;
54
+ duration?: number;
55
+ transferSize?: number;
56
+ initiatorType?: string;
57
+ initiator?: string;
58
+ responseBody?: string;
59
+ timestamp: number;
60
+ }
61
+ export interface BrowserContext {
62
+ errors: ConsoleEntry[];
63
+ networkFailures: NetworkEntry[];
64
+ layoutShifts?: Array<{
65
+ value: number;
66
+ sources?: Array<{
67
+ selector: string;
68
+ }>;
69
+ }>;
70
+ }
71
+ export interface Diagnosis {
72
+ suspected: string[];
73
+ unusual: string[];
74
+ relatedErrors: string[];
75
+ }
76
+ export interface InspectCapture {
77
+ mode: 'inspect';
78
+ intent: Intent;
79
+ timestamp: number;
80
+ element: {
81
+ selector: string;
82
+ tag: string;
83
+ id?: string;
84
+ classes: string[];
85
+ text: string;
86
+ attributes: Record<string, string>;
87
+ rect: ElementRect;
88
+ styles: ElementStyles;
89
+ };
90
+ parent: ParentInfo;
91
+ siblings: SiblingInfo[];
92
+ cssRules: CSSRule[];
93
+ browserContext: BrowserContext;
94
+ diagnosis: Diagnosis;
95
+ screenshot?: string;
96
+ }
97
+ export interface FreeSelectCapture {
98
+ mode: 'free-select';
99
+ intent: Intent;
100
+ timestamp: number;
101
+ region: ElementRect;
102
+ screenshot: string;
103
+ elements: Array<{
104
+ selector: string;
105
+ tag: string;
106
+ text: string;
107
+ role: string;
108
+ styles: Record<string, unknown>;
109
+ hasInteractionStates: boolean;
110
+ }>;
111
+ structure: string;
112
+ aestheticAnalysis?: {
113
+ issues: string[];
114
+ suggestions: string[];
115
+ colorPalette: string[];
116
+ };
117
+ browserContext: BrowserContext;
118
+ }
119
+ export interface FlowEvent {
120
+ time: number;
121
+ type: EventType;
122
+ data: Record<string, unknown>;
123
+ }
124
+ export interface FlowRecording {
125
+ mode: 'flow';
126
+ duration: number;
127
+ startTime: number;
128
+ events: FlowEvent[];
129
+ finalSelection?: InspectCapture | FreeSelectCapture;
130
+ summary: {
131
+ totalEvents: number;
132
+ clicks: number;
133
+ inputs: number;
134
+ scrolls: number;
135
+ navigations: number;
136
+ networkRequests: number;
137
+ networkErrors: number;
138
+ consoleErrors: number;
139
+ layoutShifts: number;
140
+ };
141
+ diagnosis: {
142
+ suspectedIssue: string;
143
+ timeline: string;
144
+ rootCause?: string;
145
+ };
146
+ }
147
+ export interface PageDiagnostics {
148
+ mode: 'diagnostics';
149
+ url: string;
150
+ timestamp: number;
151
+ errors: Array<{
152
+ message: string;
153
+ source: string;
154
+ count: number;
155
+ }>;
156
+ networkFailures: Array<{
157
+ url: string;
158
+ method: string;
159
+ status: number;
160
+ statusText: string;
161
+ }>;
162
+ performance: {
163
+ lcp?: {
164
+ value: number;
165
+ element: string;
166
+ };
167
+ cls: {
168
+ value: number;
169
+ shifts: Array<{
170
+ element: string;
171
+ delta: number;
172
+ }>;
173
+ };
174
+ longTasks: Array<{
175
+ duration: number;
176
+ startTime: number;
177
+ }>;
178
+ };
179
+ accessibility: {
180
+ missingAltText: number;
181
+ lowContrast: number;
182
+ missingLabels: number;
183
+ };
184
+ warnings: string[];
185
+ }
186
+ export interface DOMSnapshot {
187
+ id: string;
188
+ timestamp: number;
189
+ url: string;
190
+ selector?: string;
191
+ elements: Map<string, {
192
+ selector: string;
193
+ classes: string[];
194
+ size: {
195
+ width: number;
196
+ height: number;
197
+ };
198
+ inlineStyles: string;
199
+ }>;
200
+ }
201
+ export interface DOMDiff {
202
+ before: string;
203
+ after: string;
204
+ changes: Array<{
205
+ selector: string;
206
+ type: 'added' | 'removed' | 'changed';
207
+ changes?: {
208
+ classes?: {
209
+ added: string[];
210
+ removed: string[];
211
+ };
212
+ size?: {
213
+ before: {
214
+ width: number;
215
+ height: number;
216
+ };
217
+ after: {
218
+ width: number;
219
+ height: number;
220
+ };
221
+ };
222
+ styles?: Array<{
223
+ property: string;
224
+ before: string;
225
+ after: string;
226
+ }>;
227
+ };
228
+ }>;
229
+ }
230
+ export interface ToolResponse {
231
+ content: Array<{
232
+ type: 'text' | 'image';
233
+ text?: string;
234
+ data?: string;
235
+ mimeType?: string;
236
+ }>;
237
+ isError?: boolean;
238
+ }
239
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC;AAGlD,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,YAAY,GACZ,OAAO,GACP,OAAO,GACP,QAAQ,GACR,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,GACf,aAAa,GACb,cAAc,GACd,eAAe,GACf,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,UAAU,GACV,YAAY,GACZ,WAAW,GACX,WAAW,GACX,OAAO,GACP,MAAM,CAAC;AAGX,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,eAAe,EAAE,YAAY,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACvC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,EAAE,WAAW,CAAC;QAClB,MAAM,EAAE,aAAa,CAAC;KACvB,CAAC;IACF,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,oBAAoB,EAAE,OAAO,CAAC;KAC/B,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE;QAClB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;IACF,cAAc,EAAE,cAAc,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,GAAG,iBAAiB,CAAC;IACpD,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,SAAS,EAAE;QACT,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,eAAe,EAAE,KAAK,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,WAAW,EAAE;QACX,GAAG,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QACzC,GAAG,EAAE;YACH,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,KAAK,CAAC;gBAAE,OAAO,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SACnD,CAAC;QACF,SAAS,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3D,CAAC;IACF,aAAa,EAAE;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QACxC,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;QACtC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,EAAE;gBAAE,KAAK,EAAE,MAAM,EAAE,CAAC;gBAAC,OAAO,EAAE,MAAM,EAAE,CAAA;aAAE,CAAC;YACjD,IAAI,CAAC,EAAE;gBAAE,MAAM,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAC;oBAAC,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAC;oBAAC,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,CAAC;YAC/F,MAAM,CAAC,EAAE,KAAK,CAAC;gBAAE,QAAQ,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SACrE,CAAC;KACH,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Server Types
4
+ * Mirrors extension types for communication
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * WebSocket Server for Chrome Extension Communication
3
+ */
4
+ import { WebSocketServer } from 'ws';
5
+ import type { InspectCapture, FreeSelectCapture, FlowRecording, PageDiagnostics, DOMSnapshot, DOMDiff } from './types/index.js';
6
+ /**
7
+ * Create and start WebSocket server
8
+ */
9
+ export declare function createWebSocketServer(port?: number): Promise<WebSocketServer | null>;
10
+ /**
11
+ * Check if extension is connected
12
+ * Checks both in-memory connection and shared state file
13
+ */
14
+ export declare function isExtensionConnected(): boolean;
15
+ /**
16
+ * Get current selection
17
+ */
18
+ export declare function getCurrentSelection(): InspectCapture | FreeSelectCapture | null;
19
+ /**
20
+ * Clear current selection
21
+ */
22
+ export declare function clearSelection(): void;
23
+ /**
24
+ * Get current recording
25
+ */
26
+ export declare function getCurrentRecording(): FlowRecording | null;
27
+ /**
28
+ * Check if recording is in progress
29
+ */
30
+ export declare function isRecordingActive(): boolean;
31
+ /**
32
+ * Request page diagnostics from extension
33
+ */
34
+ export declare function requestDiagnostics(): Promise<PageDiagnostics>;
35
+ /**
36
+ * Start flow recording
37
+ */
38
+ export declare function startRecording(): Promise<void>;
39
+ /**
40
+ * Stop flow recording
41
+ */
42
+ export declare function stopRecording(): Promise<FlowRecording>;
43
+ /**
44
+ * Request recent activity buffer from extension
45
+ */
46
+ export declare function requestRecentActivity(): Promise<FlowRecording | null>;
47
+ /**
48
+ * Request DOM snapshot
49
+ */
50
+ export declare function requestSnapshot(selector?: string): Promise<DOMSnapshot>;
51
+ /**
52
+ * Request DOM diff
53
+ */
54
+ export declare function requestDiff(before: string, after: string): Promise<DOMDiff>;
55
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAa,MAAM,IAAI,CAAC;AAYhD,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,WAAW,EACX,OAAO,EACR,MAAM,kBAAkB,CAAC;AAyL1B;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,SAAe,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwF1F;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAW9C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,GAAG,iBAAiB,GAAG,IAAI,CAO/E;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,GAAG,IAAI,CAO1D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAK3C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAEnE;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAMpD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAO5D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAE3E;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE7E;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEjF"}
@@ -0,0 +1,355 @@
1
+ "use strict";
2
+ /**
3
+ * WebSocket Server for Chrome Extension Communication
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createWebSocketServer = createWebSocketServer;
7
+ exports.isExtensionConnected = isExtensionConnected;
8
+ exports.getCurrentSelection = getCurrentSelection;
9
+ exports.clearSelection = clearSelection;
10
+ exports.getCurrentRecording = getCurrentRecording;
11
+ exports.isRecordingActive = isRecordingActive;
12
+ exports.requestDiagnostics = requestDiagnostics;
13
+ exports.startRecording = startRecording;
14
+ exports.stopRecording = stopRecording;
15
+ exports.requestRecentActivity = requestRecentActivity;
16
+ exports.requestSnapshot = requestSnapshot;
17
+ exports.requestDiff = requestDiff;
18
+ const ws_1 = require("ws");
19
+ const http_1 = require("http");
20
+ const shared_state_js_1 = require("./shared-state.js");
21
+ const DEFAULT_PORT = 7007;
22
+ // Current state from extension
23
+ let currentSelection = null;
24
+ let currentRecording = null;
25
+ let isRecording = false;
26
+ let connectedClient = null; // Extension connection (server mode)
27
+ let relayClient = null; // Connection to primary server (relay mode)
28
+ let relayClients = new Set(); // Relay MCP clients connected to this server
29
+ // Pending requests waiting for response
30
+ const pendingRequests = new Map();
31
+ let requestIdCounter = 0;
32
+ /**
33
+ * Generate unique request ID
34
+ */
35
+ function generateRequestId() {
36
+ return `req_${++requestIdCounter}_${Date.now()}`;
37
+ }
38
+ /**
39
+ * Send request to extension and wait for response
40
+ */
41
+ async function sendRequest(type, payload, timeoutMs = 10000) {
42
+ return new Promise((resolve, reject) => {
43
+ // Determine which connection to use: direct extension or relay
44
+ const target = (connectedClient && connectedClient.readyState === ws_1.WebSocket.OPEN)
45
+ ? connectedClient
46
+ : (relayClient && relayClient.readyState === ws_1.WebSocket.OPEN)
47
+ ? relayClient
48
+ : null;
49
+ if (!target) {
50
+ reject(new Error('Extension not connected. The browser extension WebSocket may have disconnected. Try reloading the extension or refreshing the page.'));
51
+ return;
52
+ }
53
+ const id = generateRequestId();
54
+ const timeout = setTimeout(() => {
55
+ pendingRequests.delete(id);
56
+ reject(new Error(`Request timed out: ${type}`));
57
+ }, timeoutMs);
58
+ pendingRequests.set(id, { resolve: resolve, reject, timeout });
59
+ target.send(JSON.stringify({ type, id, payload }));
60
+ });
61
+ }
62
+ /**
63
+ * Handle incoming message from extension
64
+ */
65
+ function handleMessage(data) {
66
+ try {
67
+ const message = JSON.parse(data);
68
+ // Handle response to pending request
69
+ if (message.id && pendingRequests.has(message.id)) {
70
+ const pending = pendingRequests.get(message.id);
71
+ clearTimeout(pending.timeout);
72
+ pendingRequests.delete(message.id);
73
+ if (message.error) {
74
+ pending.reject(new Error(message.error));
75
+ }
76
+ else {
77
+ pending.resolve(message.payload);
78
+ }
79
+ return;
80
+ }
81
+ // Handle push messages from extension
82
+ switch (message.type) {
83
+ case 'ELEMENT_SELECTED':
84
+ currentSelection = message.payload;
85
+ (0, shared_state_js_1.saveSelection)(currentSelection); // Save to shared state
86
+ console.error('[MCP] Element selected:', currentSelection.element.selector);
87
+ break;
88
+ case 'REGION_SELECTED':
89
+ currentSelection = message.payload;
90
+ (0, shared_state_js_1.saveSelection)(currentSelection); // Save to shared state
91
+ console.error('[MCP] Region selected:', `${currentSelection.region.width}x${currentSelection.region.height}`);
92
+ break;
93
+ case 'RECORDING_STOPPED':
94
+ currentRecording = message.payload;
95
+ isRecording = false;
96
+ (0, shared_state_js_1.saveRecording)(currentRecording); // Save to shared state
97
+ (0, shared_state_js_1.saveRecordingState)(false); // Save recording state
98
+ console.error('[MCP] Recording stopped:', currentRecording?.summary.totalEvents, 'events');
99
+ break;
100
+ case 'BUFFER_RECORDING':
101
+ currentRecording = message.payload;
102
+ (0, shared_state_js_1.saveRecording)(currentRecording);
103
+ console.error('[MCP] Buffer recording received:', currentRecording?.summary.totalEvents, 'events');
104
+ break;
105
+ case 'PONG':
106
+ // Connection alive
107
+ break;
108
+ default:
109
+ console.error('[MCP] Unknown message type:', message.type);
110
+ }
111
+ }
112
+ catch (error) {
113
+ console.error('[MCP] Failed to parse message:', error);
114
+ }
115
+ }
116
+ /**
117
+ * Connect as a relay client to an existing WebSocket server
118
+ */
119
+ function connectAsRelay(port) {
120
+ if (relayClient && relayClient.readyState === ws_1.WebSocket.OPEN)
121
+ return;
122
+ try {
123
+ relayClient = new ws_1.WebSocket(`ws://localhost:${port}`);
124
+ relayClient.on('open', () => {
125
+ console.error('[MCP] Connected as relay client to primary server');
126
+ // Identify ourselves as a relay client
127
+ relayClient.send(JSON.stringify({ type: 'MCP_RELAY_IDENTIFY' }));
128
+ });
129
+ relayClient.on('message', (data) => {
130
+ handleMessage(data.toString());
131
+ });
132
+ relayClient.on('close', () => {
133
+ console.error('[MCP] Relay connection closed, reconnecting...');
134
+ relayClient = null;
135
+ setTimeout(() => connectAsRelay(port), 3000);
136
+ });
137
+ relayClient.on('error', () => {
138
+ // Error will be followed by close event
139
+ });
140
+ }
141
+ catch (error) {
142
+ console.error('[MCP] Failed to connect as relay:', error);
143
+ setTimeout(() => connectAsRelay(port), 3000);
144
+ }
145
+ }
146
+ /**
147
+ * Handle a message from a relay client (on the server side)
148
+ * Forwards requests to the extension and relays responses back
149
+ */
150
+ function handleRelayRequest(relayWs, message) {
151
+ if (!message.id || !connectedClient || connectedClient.readyState !== ws_1.WebSocket.OPEN) {
152
+ // Can't forward without an id or without extension connection
153
+ if (message.id) {
154
+ relayWs.send(JSON.stringify({ id: message.id, error: 'Extension not connected to primary server' }));
155
+ }
156
+ return;
157
+ }
158
+ // Forward to extension with a mapped ID
159
+ const relayId = generateRequestId();
160
+ const timeout = setTimeout(() => {
161
+ pendingRequests.delete(relayId);
162
+ relayWs.send(JSON.stringify({ id: message.id, error: 'Request timed out' }));
163
+ }, 10000);
164
+ pendingRequests.set(relayId, {
165
+ resolve: (value) => {
166
+ relayWs.send(JSON.stringify({ id: message.id, payload: value }));
167
+ },
168
+ reject: (error) => {
169
+ relayWs.send(JSON.stringify({ id: message.id, error: error.message }));
170
+ },
171
+ timeout,
172
+ });
173
+ connectedClient.send(JSON.stringify({ type: message.type, id: relayId, payload: message.payload }));
174
+ }
175
+ /**
176
+ * Create and start WebSocket server
177
+ */
178
+ function createWebSocketServer(port = DEFAULT_PORT) {
179
+ return new Promise((resolve) => {
180
+ const httpServer = (0, http_1.createServer)();
181
+ // Handle port-in-use error before trying to bind
182
+ httpServer.on('error', (error) => {
183
+ if (error.code === 'EADDRINUSE') {
184
+ console.error(`[MCP] Port ${port} already in use - connecting as relay client`);
185
+ connectAsRelay(port);
186
+ resolve(null);
187
+ }
188
+ else {
189
+ console.error('[MCP] HTTP server error:', error);
190
+ resolve(null);
191
+ }
192
+ });
193
+ httpServer.listen(port, () => {
194
+ const wss = new ws_1.WebSocketServer({ server: httpServer });
195
+ console.error(`[MCP] WebSocket server listening on port ${port}`);
196
+ wss.on('connection', (ws) => {
197
+ // Default to extension — relay clients will re-identify themselves immediately
198
+ // Only replace if no existing open extension connection
199
+ if (!connectedClient || connectedClient.readyState !== ws_1.WebSocket.OPEN) {
200
+ console.error('[MCP] Extension connected');
201
+ connectedClient = ws;
202
+ (0, shared_state_js_1.saveConnectionState)(true);
203
+ }
204
+ ws.on('message', (data) => {
205
+ const msgStr = data.toString();
206
+ try {
207
+ const msg = JSON.parse(msgStr);
208
+ // Check if this client is identifying as a relay
209
+ if (msg.type === 'MCP_RELAY_IDENTIFY') {
210
+ console.error('[MCP] Client re-identified as relay');
211
+ relayClients.add(ws);
212
+ // Unset as extension if it was set
213
+ if (connectedClient === ws) {
214
+ connectedClient = null;
215
+ }
216
+ return;
217
+ }
218
+ // If it's a known relay client, forward its requests to extension
219
+ if (relayClients.has(ws)) {
220
+ handleRelayRequest(ws, msg);
221
+ return;
222
+ }
223
+ // Extension message
224
+ handleMessage(msgStr);
225
+ }
226
+ catch {
227
+ handleMessage(msgStr);
228
+ }
229
+ });
230
+ ws.on('close', () => {
231
+ if (relayClients.has(ws)) {
232
+ console.error('[MCP] Relay client disconnected');
233
+ relayClients.delete(ws);
234
+ }
235
+ else if (connectedClient === ws) {
236
+ console.error('[MCP] Extension disconnected');
237
+ connectedClient = null;
238
+ (0, shared_state_js_1.saveConnectionState)(false);
239
+ }
240
+ });
241
+ ws.on('error', (error) => {
242
+ console.error('[MCP] WebSocket error:', error);
243
+ });
244
+ // Send ping periodically and refresh connection state
245
+ const pingInterval = setInterval(() => {
246
+ if (ws.readyState === ws_1.WebSocket.OPEN && connectedClient === ws) {
247
+ ws.send(JSON.stringify({ type: 'PING' }));
248
+ (0, shared_state_js_1.saveConnectionState)(true); // Refresh timestamp
249
+ }
250
+ }, 30000);
251
+ ws.on('close', () => clearInterval(pingInterval));
252
+ });
253
+ resolve(wss);
254
+ });
255
+ });
256
+ }
257
+ /**
258
+ * Check if extension is connected
259
+ * Checks both in-memory connection and shared state file
260
+ */
261
+ function isExtensionConnected() {
262
+ // Check direct extension connection (primary server mode)
263
+ if (connectedClient !== null && connectedClient.readyState === ws_1.WebSocket.OPEN) {
264
+ return true;
265
+ }
266
+ // Check relay connection (secondary process mode)
267
+ if (relayClient !== null && relayClient.readyState === ws_1.WebSocket.OPEN) {
268
+ return true;
269
+ }
270
+ // Fall back to shared state file
271
+ return (0, shared_state_js_1.loadConnectionState)();
272
+ }
273
+ /**
274
+ * Get current selection
275
+ */
276
+ function getCurrentSelection() {
277
+ // Load from shared state to get updates from other processes
278
+ const sharedSelection = (0, shared_state_js_1.loadSelection)();
279
+ if (sharedSelection) {
280
+ currentSelection = sharedSelection;
281
+ }
282
+ return currentSelection;
283
+ }
284
+ /**
285
+ * Clear current selection
286
+ */
287
+ function clearSelection() {
288
+ currentSelection = null;
289
+ }
290
+ /**
291
+ * Get current recording
292
+ */
293
+ function getCurrentRecording() {
294
+ // Load from shared state to get updates from other processes
295
+ const sharedRecording = (0, shared_state_js_1.loadRecording)();
296
+ if (sharedRecording) {
297
+ currentRecording = sharedRecording;
298
+ }
299
+ return currentRecording;
300
+ }
301
+ /**
302
+ * Check if recording is in progress
303
+ */
304
+ function isRecordingActive() {
305
+ // Load from shared state to get updates from other processes
306
+ const sharedState = (0, shared_state_js_1.loadRecordingState)();
307
+ isRecording = sharedState;
308
+ return isRecording;
309
+ }
310
+ /**
311
+ * Request page diagnostics from extension
312
+ */
313
+ async function requestDiagnostics() {
314
+ return sendRequest('GET_DIAGNOSTICS');
315
+ }
316
+ /**
317
+ * Start flow recording
318
+ */
319
+ async function startRecording() {
320
+ await sendRequest('START_RECORDING');
321
+ isRecording = true;
322
+ currentRecording = null;
323
+ (0, shared_state_js_1.saveRecordingState)(true); // Save to shared state
324
+ (0, shared_state_js_1.saveRecording)(null); // Clear recording
325
+ }
326
+ /**
327
+ * Stop flow recording
328
+ */
329
+ async function stopRecording() {
330
+ const recording = await sendRequest('STOP_RECORDING');
331
+ isRecording = false;
332
+ currentRecording = recording;
333
+ (0, shared_state_js_1.saveRecordingState)(false); // Save to shared state
334
+ (0, shared_state_js_1.saveRecording)(recording); // Save recording
335
+ return recording;
336
+ }
337
+ /**
338
+ * Request recent activity buffer from extension
339
+ */
340
+ async function requestRecentActivity() {
341
+ return sendRequest('GET_RECENT_ACTIVITY');
342
+ }
343
+ /**
344
+ * Request DOM snapshot
345
+ */
346
+ async function requestSnapshot(selector) {
347
+ return sendRequest('SNAPSHOT_DOM', { selector });
348
+ }
349
+ /**
350
+ * Request DOM diff
351
+ */
352
+ async function requestDiff(before, after) {
353
+ return sendRequest('DIFF_SNAPSHOTS', { before, after });
354
+ }
355
+ //# sourceMappingURL=websocket.js.map