agentgui 1.0.274 → 1.0.276
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/CLAUDE.md +280 -280
- package/IPFS_DOWNLOADER.md +277 -277
- package/TASK_2C_COMPLETION.md +334 -334
- package/agentgui.ico +0 -0
- package/bin/gmgui.cjs +54 -54
- package/build-portable.js +13 -42
- package/database.js +1422 -1406
- package/lib/claude-runner.js +1130 -1130
- package/lib/ipfs-downloader.js +459 -459
- package/lib/speech.js +159 -152
- package/package.json +1 -1
- package/readme.md +76 -76
- package/server.js +3787 -3794
- package/setup-npm-token.sh +68 -68
- package/static/app.js +773 -773
- package/static/event-rendering-showcase.html +708 -708
- package/static/index.html +3178 -3180
- package/static/js/agent-auth.js +298 -298
- package/static/js/audio-recorder-processor.js +18 -18
- package/static/js/client.js +2656 -2656
- package/static/js/conversations.js +583 -583
- package/static/js/dialogs.js +267 -267
- package/static/js/event-consolidator.js +101 -101
- package/static/js/event-filter.js +311 -311
- package/static/js/event-processor.js +452 -452
- package/static/js/features.js +413 -413
- package/static/js/kalman-filter.js +67 -67
- package/static/js/progress-dialog.js +130 -130
- package/static/js/script-runner.js +219 -219
- package/static/js/streaming-renderer.js +2123 -2120
- package/static/js/syntax-highlighter.js +269 -269
- package/static/js/tts-websocket-handler.js +152 -152
- package/static/js/ui-components.js +431 -431
- package/static/js/voice.js +849 -849
- package/static/js/websocket-manager.js +596 -596
- package/static/templates/INDEX.html +465 -465
- package/static/templates/README.md +190 -190
- package/static/templates/agent-capabilities.html +56 -56
- package/static/templates/agent-metadata-panel.html +44 -44
- package/static/templates/agent-status-badge.html +30 -30
- package/static/templates/code-annotation-panel.html +155 -155
- package/static/templates/code-suggestion-panel.html +184 -184
- package/static/templates/command-header.html +77 -77
- package/static/templates/command-output-scrollable.html +118 -118
- package/static/templates/elapsed-time.html +54 -54
- package/static/templates/error-alert.html +106 -106
- package/static/templates/error-history-timeline.html +160 -160
- package/static/templates/error-recovery-options.html +109 -109
- package/static/templates/error-stack-trace.html +95 -95
- package/static/templates/error-summary.html +80 -80
- package/static/templates/event-counter.html +48 -48
- package/static/templates/execution-actions.html +97 -97
- package/static/templates/execution-progress-bar.html +80 -80
- package/static/templates/execution-stepper.html +120 -120
- package/static/templates/file-breadcrumb.html +118 -118
- package/static/templates/file-diff-viewer.html +121 -121
- package/static/templates/file-metadata.html +133 -133
- package/static/templates/file-read-panel.html +66 -66
- package/static/templates/file-write-panel.html +120 -120
- package/static/templates/git-branch-remote.html +107 -107
- package/static/templates/git-diff-list.html +101 -101
- package/static/templates/git-log-visualization.html +153 -153
- package/static/templates/git-status-panel.html +115 -115
- package/static/templates/quality-metrics-display.html +170 -170
- package/static/templates/terminal-output-panel.html +87 -87
- package/static/templates/test-results-display.html +144 -144
- package/static/theme.js +72 -72
- package/test-download-progress.js +223 -223
- package/test-websocket-broadcast.js +147 -147
- package/tests/ipfs-downloader.test.js +370 -370
|
@@ -1,452 +1,452 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event Processor
|
|
3
|
-
* Transforms, validates, and enriches streaming events
|
|
4
|
-
* Handles ANSI colors, markdown, diffs, and other data transformations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
class EventProcessor {
|
|
8
|
-
constructor(config = {}) {
|
|
9
|
-
this.config = {
|
|
10
|
-
enableSyntaxHighlight: config.enableSyntaxHighlight !== false,
|
|
11
|
-
enableMarkdown: config.enableMarkdown !== false,
|
|
12
|
-
enableANSI: config.enableANSI !== false,
|
|
13
|
-
maxContentLength: config.maxContentLength || 100000,
|
|
14
|
-
...config
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ANSI color codes mapping
|
|
18
|
-
this.ansiCodes = {
|
|
19
|
-
reset: '\x1b[0m',
|
|
20
|
-
bold: '\x1b[1m',
|
|
21
|
-
dim: '\x1b[2m',
|
|
22
|
-
italic: '\x1b[3m',
|
|
23
|
-
underline: '\x1b[4m',
|
|
24
|
-
blink: '\x1b[5m',
|
|
25
|
-
reverse: '\x1b[7m',
|
|
26
|
-
hidden: '\x1b[8m',
|
|
27
|
-
strikethrough: '\x1b[9m',
|
|
28
|
-
// Foreground colors
|
|
29
|
-
black: '\x1b[30m',
|
|
30
|
-
red: '\x1b[31m',
|
|
31
|
-
green: '\x1b[32m',
|
|
32
|
-
yellow: '\x1b[33m',
|
|
33
|
-
blue: '\x1b[34m',
|
|
34
|
-
magenta: '\x1b[35m',
|
|
35
|
-
cyan: '\x1b[36m',
|
|
36
|
-
white: '\x1b[37m',
|
|
37
|
-
// Background colors
|
|
38
|
-
bgBlack: '\x1b[40m',
|
|
39
|
-
bgRed: '\x1b[41m',
|
|
40
|
-
bgGreen: '\x1b[42m',
|
|
41
|
-
bgYellow: '\x1b[43m',
|
|
42
|
-
bgBlue: '\x1b[44m',
|
|
43
|
-
bgMagenta: '\x1b[45m',
|
|
44
|
-
bgCyan: '\x1b[46m',
|
|
45
|
-
bgWhite: '\x1b[47m'
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// CSS color mapping
|
|
49
|
-
this.colorMap = {
|
|
50
|
-
'30': '#000000', // black
|
|
51
|
-
'31': '#ff6b6b', // red
|
|
52
|
-
'32': '#51cf66', // green
|
|
53
|
-
'33': '#ffd43b', // yellow
|
|
54
|
-
'34': '#4dabf7', // blue
|
|
55
|
-
'35': '#da77f2', // magenta
|
|
56
|
-
'36': '#20c997', // cyan
|
|
57
|
-
'37': '#ffffff', // white
|
|
58
|
-
'90': '#666666', // bright black
|
|
59
|
-
'91': '#ff8787', // bright red
|
|
60
|
-
'92': '#69db7c', // bright green
|
|
61
|
-
'93': '#ffe066', // bright yellow
|
|
62
|
-
'94': '#74c0fc', // bright blue
|
|
63
|
-
'95': '#e599f7', // bright magenta
|
|
64
|
-
'96': '#38f9d7', // bright cyan
|
|
65
|
-
'97': '#f8f9fa' // bright white
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Statistics
|
|
69
|
-
this.stats = {
|
|
70
|
-
totalEvents: 0,
|
|
71
|
-
processedEvents: 0,
|
|
72
|
-
validatedEvents: 0,
|
|
73
|
-
transformedEvents: 0,
|
|
74
|
-
errorCount: 0,
|
|
75
|
-
avgProcessTime: 0
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Process and enrich event
|
|
81
|
-
*/
|
|
82
|
-
processEvent(event) {
|
|
83
|
-
if (!event || typeof event !== 'object') {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const startTime = performance.now();
|
|
88
|
-
this.stats.totalEvents++;
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
// Validate event structure
|
|
92
|
-
if (!this.validateEvent(event)) {
|
|
93
|
-
this.stats.errorCount++;
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.stats.validatedEvents++;
|
|
98
|
-
|
|
99
|
-
// Add processing metadata
|
|
100
|
-
const processed = {
|
|
101
|
-
...event,
|
|
102
|
-
processedAt: Date.now(),
|
|
103
|
-
processTime: 0
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Transform event based on type
|
|
107
|
-
if (event.type === 'text_block' || event.type === 'code_block') {
|
|
108
|
-
processed.content = this.transformContent(event.content || '', event.type);
|
|
109
|
-
this.stats.transformedEvents++;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (event.type === 'command_execute' && event.output) {
|
|
113
|
-
processed.output = this.transformANSI(event.output);
|
|
114
|
-
this.stats.transformedEvents++;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (event.type === 'file_diff' || event.type === 'git_diff') {
|
|
118
|
-
processed.diff = this.transformDiff(event.diff || event.content || '');
|
|
119
|
-
this.stats.transformedEvents++;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
processed.processTime = performance.now() - startTime;
|
|
123
|
-
this.stats.processedEvents++;
|
|
124
|
-
|
|
125
|
-
// Update average processing time
|
|
126
|
-
this.stats.avgProcessTime = (this.stats.avgProcessTime * (this.stats.processedEvents - 1) + processed.processTime) / this.stats.processedEvents;
|
|
127
|
-
|
|
128
|
-
return processed;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error('Event processing error:', error);
|
|
131
|
-
this.stats.errorCount++;
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Validate event structure
|
|
138
|
-
*/
|
|
139
|
-
validateEvent(event) {
|
|
140
|
-
if (!event.type) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Required fields by type
|
|
145
|
-
const typeRequirements = {
|
|
146
|
-
streaming_start: ['sessionId', 'conversationId'],
|
|
147
|
-
streaming_complete: ['sessionId'],
|
|
148
|
-
file_read: ['path'],
|
|
149
|
-
file_write: ['path'],
|
|
150
|
-
command_execute: ['command'],
|
|
151
|
-
git_status: [],
|
|
152
|
-
error: ['message']
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const requirements = typeRequirements[event.type];
|
|
156
|
-
if (requirements) {
|
|
157
|
-
for (const field of requirements) {
|
|
158
|
-
if (!event[field]) {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Transform content based on type
|
|
169
|
-
*/
|
|
170
|
-
transformContent(content, type) {
|
|
171
|
-
if (typeof content !== 'string') {
|
|
172
|
-
return content;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (content.length > this.config.maxContentLength) {
|
|
176
|
-
return content.substring(0, this.config.maxContentLength) + '\n... (truncated)';
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return content;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Transform ANSI escape codes to HTML/CSS
|
|
184
|
-
*/
|
|
185
|
-
transformANSI(text) {
|
|
186
|
-
if (!this.config.enableANSI || typeof text !== 'string') {
|
|
187
|
-
return text;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let result = '';
|
|
191
|
-
let currentStyle = { fg: null, bg: null, bold: false };
|
|
192
|
-
const stack = [];
|
|
193
|
-
|
|
194
|
-
// Pattern for ANSI escape sequences
|
|
195
|
-
const pattern = /\x1b\[([0-9;]*?)m/g;
|
|
196
|
-
let lastIndex = 0;
|
|
197
|
-
let match;
|
|
198
|
-
|
|
199
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
200
|
-
// Add text before this escape sequence
|
|
201
|
-
if (match.index > lastIndex) {
|
|
202
|
-
const plainText = text.substring(lastIndex, match.index);
|
|
203
|
-
result += this.escapeHtml(plainText);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Parse ANSI code
|
|
207
|
-
const codes = match[1].split(';').map(c => parseInt(c, 10));
|
|
208
|
-
for (const code of codes) {
|
|
209
|
-
if (code === 0) {
|
|
210
|
-
// Reset
|
|
211
|
-
currentStyle = { fg: null, bg: null, bold: false };
|
|
212
|
-
} else if (code === 1) {
|
|
213
|
-
currentStyle.bold = true;
|
|
214
|
-
} else if (code >= 30 && code <= 37) {
|
|
215
|
-
currentStyle.fg = this.colorMap[code];
|
|
216
|
-
} else if (code >= 40 && code <= 47) {
|
|
217
|
-
currentStyle.bg = this.colorMap[String(code - 10)];
|
|
218
|
-
} else if (code >= 90 && code <= 97) {
|
|
219
|
-
currentStyle.fg = this.colorMap[code];
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
lastIndex = pattern.lastIndex;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Add remaining text
|
|
227
|
-
if (lastIndex < text.length) {
|
|
228
|
-
result += this.escapeHtml(text.substring(lastIndex));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Transform unified diff format
|
|
236
|
-
*/
|
|
237
|
-
transformDiff(diffText) {
|
|
238
|
-
if (typeof diffText !== 'string') {
|
|
239
|
-
return diffText;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const lines = diffText.split('\n');
|
|
243
|
-
const parsed = {
|
|
244
|
-
headers: [],
|
|
245
|
-
hunks: []
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
let currentHunk = null;
|
|
249
|
-
|
|
250
|
-
for (const line of lines) {
|
|
251
|
-
if (line.startsWith('---') || line.startsWith('+++')) {
|
|
252
|
-
parsed.headers.push(line);
|
|
253
|
-
} else if (line.startsWith('@@')) {
|
|
254
|
-
if (currentHunk) {
|
|
255
|
-
parsed.hunks.push(currentHunk);
|
|
256
|
-
}
|
|
257
|
-
currentHunk = {
|
|
258
|
-
header: line,
|
|
259
|
-
changes: []
|
|
260
|
-
};
|
|
261
|
-
} else if (currentHunk) {
|
|
262
|
-
if (line.startsWith('-')) {
|
|
263
|
-
currentHunk.changes.push({ type: 'deleted', line: line.substring(1) });
|
|
264
|
-
} else if (line.startsWith('+')) {
|
|
265
|
-
currentHunk.changes.push({ type: 'added', line: line.substring(1) });
|
|
266
|
-
} else if (line.startsWith(' ')) {
|
|
267
|
-
currentHunk.changes.push({ type: 'context', line: line.substring(1) });
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (currentHunk) {
|
|
273
|
-
parsed.hunks.push(currentHunk);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return parsed;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Convert markdown to HTML (simple implementation)
|
|
281
|
-
*/
|
|
282
|
-
transformMarkdown(markdown) {
|
|
283
|
-
if (!this.config.enableMarkdown || typeof markdown !== 'string') {
|
|
284
|
-
return markdown;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
let html = this.escapeHtml(markdown);
|
|
288
|
-
|
|
289
|
-
// Bold
|
|
290
|
-
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
291
|
-
|
|
292
|
-
// Italic
|
|
293
|
-
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
294
|
-
|
|
295
|
-
// Code
|
|
296
|
-
html = html.replace(/`(.+?)`/g, '<code>$1</code>');
|
|
297
|
-
|
|
298
|
-
// Links
|
|
299
|
-
html = html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank">$1</a>');
|
|
300
|
-
|
|
301
|
-
// Headings
|
|
302
|
-
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
|
303
|
-
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
|
304
|
-
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
|
305
|
-
|
|
306
|
-
// Line breaks
|
|
307
|
-
html = html.replace(/\n/g, '<br>');
|
|
308
|
-
|
|
309
|
-
return html;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Detect language from content or hint
|
|
314
|
-
*/
|
|
315
|
-
detectLanguage(content, hint = null) {
|
|
316
|
-
if (hint) {
|
|
317
|
-
return hint.toLowerCase();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Simple language detection based on shebang or content patterns
|
|
321
|
-
if (content.startsWith('#!/')) {
|
|
322
|
-
if (content.includes('python')) return 'python';
|
|
323
|
-
if (content.includes('node') || content.includes('javascript')) return 'javascript';
|
|
324
|
-
if (content.includes('bash') || content.includes('sh')) return 'bash';
|
|
325
|
-
if (content.includes('ruby')) return 'ruby';
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Pattern detection
|
|
329
|
-
if (content.includes('def ') && content.includes(':')) return 'python';
|
|
330
|
-
if (content.includes('function') || content.includes('=>')) return 'javascript';
|
|
331
|
-
if (content.includes('fn ') && content.includes('->')) return 'rust';
|
|
332
|
-
if (content.includes('public static void') || content.includes('class ')) return 'java';
|
|
333
|
-
|
|
334
|
-
return 'plaintext';
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Parse JSON safely
|
|
339
|
-
*/
|
|
340
|
-
parseJSON(jsonStr) {
|
|
341
|
-
try {
|
|
342
|
-
return JSON.parse(jsonStr);
|
|
343
|
-
} catch (e) {
|
|
344
|
-
console.error('JSON parse error:', e);
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Format JSON for display
|
|
351
|
-
*/
|
|
352
|
-
formatJSON(obj, indent = 2) {
|
|
353
|
-
try {
|
|
354
|
-
return JSON.stringify(obj, null, indent);
|
|
355
|
-
} catch (e) {
|
|
356
|
-
return String(obj);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Extract file extension
|
|
362
|
-
*/
|
|
363
|
-
getFileExtension(filePath) {
|
|
364
|
-
const match = filePath.match(/\.([^.]+)$/);
|
|
365
|
-
return match ? match[1].toLowerCase() : null;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Determine syntax highlighter language from file extension
|
|
370
|
-
*/
|
|
371
|
-
getLanguageFromExtension(ext) {
|
|
372
|
-
const extMap = {
|
|
373
|
-
'js': 'javascript',
|
|
374
|
-
'jsx': 'jsx',
|
|
375
|
-
'ts': 'typescript',
|
|
376
|
-
'tsx': 'typescript',
|
|
377
|
-
'py': 'python',
|
|
378
|
-
'java': 'java',
|
|
379
|
-
'cpp': 'cpp',
|
|
380
|
-
'c': 'c',
|
|
381
|
-
'cs': 'csharp',
|
|
382
|
-
'php': 'php',
|
|
383
|
-
'rb': 'ruby',
|
|
384
|
-
'go': 'go',
|
|
385
|
-
'rs': 'rust',
|
|
386
|
-
'json': 'json',
|
|
387
|
-
'xml': 'xml',
|
|
388
|
-
'html': 'html',
|
|
389
|
-
'css': 'css',
|
|
390
|
-
'scss': 'scss',
|
|
391
|
-
'yaml': 'yaml',
|
|
392
|
-
'yml': 'yaml',
|
|
393
|
-
'sql': 'sql',
|
|
394
|
-
'sh': 'bash',
|
|
395
|
-
'bash': 'bash',
|
|
396
|
-
'zsh': 'bash'
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
return extMap[ext?.toLowerCase()] || 'plaintext';
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Truncate text with ellipsis
|
|
404
|
-
*/
|
|
405
|
-
truncateText(text, maxLength = 200) {
|
|
406
|
-
if (text.length <= maxLength) {
|
|
407
|
-
return text;
|
|
408
|
-
}
|
|
409
|
-
return text.substring(0, maxLength) + '...';
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* HTML escape utility
|
|
414
|
-
*/
|
|
415
|
-
escapeHtml(text) {
|
|
416
|
-
return window._escHtml(text);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Format timestamp
|
|
421
|
-
*/
|
|
422
|
-
formatTimestamp(timestamp) {
|
|
423
|
-
const date = new Date(timestamp);
|
|
424
|
-
return date.toLocaleTimeString();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Get statistics
|
|
429
|
-
*/
|
|
430
|
-
getStats() {
|
|
431
|
-
return { ...this.stats };
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Reset statistics
|
|
436
|
-
*/
|
|
437
|
-
resetStats() {
|
|
438
|
-
this.stats = {
|
|
439
|
-
totalEvents: 0,
|
|
440
|
-
processedEvents: 0,
|
|
441
|
-
validatedEvents: 0,
|
|
442
|
-
transformedEvents: 0,
|
|
443
|
-
errorCount: 0,
|
|
444
|
-
avgProcessTime: 0
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Export for use in browser
|
|
450
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
451
|
-
module.exports = EventProcessor;
|
|
452
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Event Processor
|
|
3
|
+
* Transforms, validates, and enriches streaming events
|
|
4
|
+
* Handles ANSI colors, markdown, diffs, and other data transformations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class EventProcessor {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
this.config = {
|
|
10
|
+
enableSyntaxHighlight: config.enableSyntaxHighlight !== false,
|
|
11
|
+
enableMarkdown: config.enableMarkdown !== false,
|
|
12
|
+
enableANSI: config.enableANSI !== false,
|
|
13
|
+
maxContentLength: config.maxContentLength || 100000,
|
|
14
|
+
...config
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ANSI color codes mapping
|
|
18
|
+
this.ansiCodes = {
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
bold: '\x1b[1m',
|
|
21
|
+
dim: '\x1b[2m',
|
|
22
|
+
italic: '\x1b[3m',
|
|
23
|
+
underline: '\x1b[4m',
|
|
24
|
+
blink: '\x1b[5m',
|
|
25
|
+
reverse: '\x1b[7m',
|
|
26
|
+
hidden: '\x1b[8m',
|
|
27
|
+
strikethrough: '\x1b[9m',
|
|
28
|
+
// Foreground colors
|
|
29
|
+
black: '\x1b[30m',
|
|
30
|
+
red: '\x1b[31m',
|
|
31
|
+
green: '\x1b[32m',
|
|
32
|
+
yellow: '\x1b[33m',
|
|
33
|
+
blue: '\x1b[34m',
|
|
34
|
+
magenta: '\x1b[35m',
|
|
35
|
+
cyan: '\x1b[36m',
|
|
36
|
+
white: '\x1b[37m',
|
|
37
|
+
// Background colors
|
|
38
|
+
bgBlack: '\x1b[40m',
|
|
39
|
+
bgRed: '\x1b[41m',
|
|
40
|
+
bgGreen: '\x1b[42m',
|
|
41
|
+
bgYellow: '\x1b[43m',
|
|
42
|
+
bgBlue: '\x1b[44m',
|
|
43
|
+
bgMagenta: '\x1b[45m',
|
|
44
|
+
bgCyan: '\x1b[46m',
|
|
45
|
+
bgWhite: '\x1b[47m'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// CSS color mapping
|
|
49
|
+
this.colorMap = {
|
|
50
|
+
'30': '#000000', // black
|
|
51
|
+
'31': '#ff6b6b', // red
|
|
52
|
+
'32': '#51cf66', // green
|
|
53
|
+
'33': '#ffd43b', // yellow
|
|
54
|
+
'34': '#4dabf7', // blue
|
|
55
|
+
'35': '#da77f2', // magenta
|
|
56
|
+
'36': '#20c997', // cyan
|
|
57
|
+
'37': '#ffffff', // white
|
|
58
|
+
'90': '#666666', // bright black
|
|
59
|
+
'91': '#ff8787', // bright red
|
|
60
|
+
'92': '#69db7c', // bright green
|
|
61
|
+
'93': '#ffe066', // bright yellow
|
|
62
|
+
'94': '#74c0fc', // bright blue
|
|
63
|
+
'95': '#e599f7', // bright magenta
|
|
64
|
+
'96': '#38f9d7', // bright cyan
|
|
65
|
+
'97': '#f8f9fa' // bright white
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Statistics
|
|
69
|
+
this.stats = {
|
|
70
|
+
totalEvents: 0,
|
|
71
|
+
processedEvents: 0,
|
|
72
|
+
validatedEvents: 0,
|
|
73
|
+
transformedEvents: 0,
|
|
74
|
+
errorCount: 0,
|
|
75
|
+
avgProcessTime: 0
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Process and enrich event
|
|
81
|
+
*/
|
|
82
|
+
processEvent(event) {
|
|
83
|
+
if (!event || typeof event !== 'object') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const startTime = performance.now();
|
|
88
|
+
this.stats.totalEvents++;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Validate event structure
|
|
92
|
+
if (!this.validateEvent(event)) {
|
|
93
|
+
this.stats.errorCount++;
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.stats.validatedEvents++;
|
|
98
|
+
|
|
99
|
+
// Add processing metadata
|
|
100
|
+
const processed = {
|
|
101
|
+
...event,
|
|
102
|
+
processedAt: Date.now(),
|
|
103
|
+
processTime: 0
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Transform event based on type
|
|
107
|
+
if (event.type === 'text_block' || event.type === 'code_block') {
|
|
108
|
+
processed.content = this.transformContent(event.content || '', event.type);
|
|
109
|
+
this.stats.transformedEvents++;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (event.type === 'command_execute' && event.output) {
|
|
113
|
+
processed.output = this.transformANSI(event.output);
|
|
114
|
+
this.stats.transformedEvents++;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (event.type === 'file_diff' || event.type === 'git_diff') {
|
|
118
|
+
processed.diff = this.transformDiff(event.diff || event.content || '');
|
|
119
|
+
this.stats.transformedEvents++;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
processed.processTime = performance.now() - startTime;
|
|
123
|
+
this.stats.processedEvents++;
|
|
124
|
+
|
|
125
|
+
// Update average processing time
|
|
126
|
+
this.stats.avgProcessTime = (this.stats.avgProcessTime * (this.stats.processedEvents - 1) + processed.processTime) / this.stats.processedEvents;
|
|
127
|
+
|
|
128
|
+
return processed;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Event processing error:', error);
|
|
131
|
+
this.stats.errorCount++;
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Validate event structure
|
|
138
|
+
*/
|
|
139
|
+
validateEvent(event) {
|
|
140
|
+
if (!event.type) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Required fields by type
|
|
145
|
+
const typeRequirements = {
|
|
146
|
+
streaming_start: ['sessionId', 'conversationId'],
|
|
147
|
+
streaming_complete: ['sessionId'],
|
|
148
|
+
file_read: ['path'],
|
|
149
|
+
file_write: ['path'],
|
|
150
|
+
command_execute: ['command'],
|
|
151
|
+
git_status: [],
|
|
152
|
+
error: ['message']
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const requirements = typeRequirements[event.type];
|
|
156
|
+
if (requirements) {
|
|
157
|
+
for (const field of requirements) {
|
|
158
|
+
if (!event[field]) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Transform content based on type
|
|
169
|
+
*/
|
|
170
|
+
transformContent(content, type) {
|
|
171
|
+
if (typeof content !== 'string') {
|
|
172
|
+
return content;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (content.length > this.config.maxContentLength) {
|
|
176
|
+
return content.substring(0, this.config.maxContentLength) + '\n... (truncated)';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return content;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Transform ANSI escape codes to HTML/CSS
|
|
184
|
+
*/
|
|
185
|
+
transformANSI(text) {
|
|
186
|
+
if (!this.config.enableANSI || typeof text !== 'string') {
|
|
187
|
+
return text;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let result = '';
|
|
191
|
+
let currentStyle = { fg: null, bg: null, bold: false };
|
|
192
|
+
const stack = [];
|
|
193
|
+
|
|
194
|
+
// Pattern for ANSI escape sequences
|
|
195
|
+
const pattern = /\x1b\[([0-9;]*?)m/g;
|
|
196
|
+
let lastIndex = 0;
|
|
197
|
+
let match;
|
|
198
|
+
|
|
199
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
200
|
+
// Add text before this escape sequence
|
|
201
|
+
if (match.index > lastIndex) {
|
|
202
|
+
const plainText = text.substring(lastIndex, match.index);
|
|
203
|
+
result += this.escapeHtml(plainText);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Parse ANSI code
|
|
207
|
+
const codes = match[1].split(';').map(c => parseInt(c, 10));
|
|
208
|
+
for (const code of codes) {
|
|
209
|
+
if (code === 0) {
|
|
210
|
+
// Reset
|
|
211
|
+
currentStyle = { fg: null, bg: null, bold: false };
|
|
212
|
+
} else if (code === 1) {
|
|
213
|
+
currentStyle.bold = true;
|
|
214
|
+
} else if (code >= 30 && code <= 37) {
|
|
215
|
+
currentStyle.fg = this.colorMap[code];
|
|
216
|
+
} else if (code >= 40 && code <= 47) {
|
|
217
|
+
currentStyle.bg = this.colorMap[String(code - 10)];
|
|
218
|
+
} else if (code >= 90 && code <= 97) {
|
|
219
|
+
currentStyle.fg = this.colorMap[code];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
lastIndex = pattern.lastIndex;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Add remaining text
|
|
227
|
+
if (lastIndex < text.length) {
|
|
228
|
+
result += this.escapeHtml(text.substring(lastIndex));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Transform unified diff format
|
|
236
|
+
*/
|
|
237
|
+
transformDiff(diffText) {
|
|
238
|
+
if (typeof diffText !== 'string') {
|
|
239
|
+
return diffText;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const lines = diffText.split('\n');
|
|
243
|
+
const parsed = {
|
|
244
|
+
headers: [],
|
|
245
|
+
hunks: []
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
let currentHunk = null;
|
|
249
|
+
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
if (line.startsWith('---') || line.startsWith('+++')) {
|
|
252
|
+
parsed.headers.push(line);
|
|
253
|
+
} else if (line.startsWith('@@')) {
|
|
254
|
+
if (currentHunk) {
|
|
255
|
+
parsed.hunks.push(currentHunk);
|
|
256
|
+
}
|
|
257
|
+
currentHunk = {
|
|
258
|
+
header: line,
|
|
259
|
+
changes: []
|
|
260
|
+
};
|
|
261
|
+
} else if (currentHunk) {
|
|
262
|
+
if (line.startsWith('-')) {
|
|
263
|
+
currentHunk.changes.push({ type: 'deleted', line: line.substring(1) });
|
|
264
|
+
} else if (line.startsWith('+')) {
|
|
265
|
+
currentHunk.changes.push({ type: 'added', line: line.substring(1) });
|
|
266
|
+
} else if (line.startsWith(' ')) {
|
|
267
|
+
currentHunk.changes.push({ type: 'context', line: line.substring(1) });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (currentHunk) {
|
|
273
|
+
parsed.hunks.push(currentHunk);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return parsed;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Convert markdown to HTML (simple implementation)
|
|
281
|
+
*/
|
|
282
|
+
transformMarkdown(markdown) {
|
|
283
|
+
if (!this.config.enableMarkdown || typeof markdown !== 'string') {
|
|
284
|
+
return markdown;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let html = this.escapeHtml(markdown);
|
|
288
|
+
|
|
289
|
+
// Bold
|
|
290
|
+
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
291
|
+
|
|
292
|
+
// Italic
|
|
293
|
+
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
294
|
+
|
|
295
|
+
// Code
|
|
296
|
+
html = html.replace(/`(.+?)`/g, '<code>$1</code>');
|
|
297
|
+
|
|
298
|
+
// Links
|
|
299
|
+
html = html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank">$1</a>');
|
|
300
|
+
|
|
301
|
+
// Headings
|
|
302
|
+
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
|
303
|
+
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
|
304
|
+
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
|
305
|
+
|
|
306
|
+
// Line breaks
|
|
307
|
+
html = html.replace(/\n/g, '<br>');
|
|
308
|
+
|
|
309
|
+
return html;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Detect language from content or hint
|
|
314
|
+
*/
|
|
315
|
+
detectLanguage(content, hint = null) {
|
|
316
|
+
if (hint) {
|
|
317
|
+
return hint.toLowerCase();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Simple language detection based on shebang or content patterns
|
|
321
|
+
if (content.startsWith('#!/')) {
|
|
322
|
+
if (content.includes('python')) return 'python';
|
|
323
|
+
if (content.includes('node') || content.includes('javascript')) return 'javascript';
|
|
324
|
+
if (content.includes('bash') || content.includes('sh')) return 'bash';
|
|
325
|
+
if (content.includes('ruby')) return 'ruby';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Pattern detection
|
|
329
|
+
if (content.includes('def ') && content.includes(':')) return 'python';
|
|
330
|
+
if (content.includes('function') || content.includes('=>')) return 'javascript';
|
|
331
|
+
if (content.includes('fn ') && content.includes('->')) return 'rust';
|
|
332
|
+
if (content.includes('public static void') || content.includes('class ')) return 'java';
|
|
333
|
+
|
|
334
|
+
return 'plaintext';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Parse JSON safely
|
|
339
|
+
*/
|
|
340
|
+
parseJSON(jsonStr) {
|
|
341
|
+
try {
|
|
342
|
+
return JSON.parse(jsonStr);
|
|
343
|
+
} catch (e) {
|
|
344
|
+
console.error('JSON parse error:', e);
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Format JSON for display
|
|
351
|
+
*/
|
|
352
|
+
formatJSON(obj, indent = 2) {
|
|
353
|
+
try {
|
|
354
|
+
return JSON.stringify(obj, null, indent);
|
|
355
|
+
} catch (e) {
|
|
356
|
+
return String(obj);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Extract file extension
|
|
362
|
+
*/
|
|
363
|
+
getFileExtension(filePath) {
|
|
364
|
+
const match = filePath.match(/\.([^.]+)$/);
|
|
365
|
+
return match ? match[1].toLowerCase() : null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Determine syntax highlighter language from file extension
|
|
370
|
+
*/
|
|
371
|
+
getLanguageFromExtension(ext) {
|
|
372
|
+
const extMap = {
|
|
373
|
+
'js': 'javascript',
|
|
374
|
+
'jsx': 'jsx',
|
|
375
|
+
'ts': 'typescript',
|
|
376
|
+
'tsx': 'typescript',
|
|
377
|
+
'py': 'python',
|
|
378
|
+
'java': 'java',
|
|
379
|
+
'cpp': 'cpp',
|
|
380
|
+
'c': 'c',
|
|
381
|
+
'cs': 'csharp',
|
|
382
|
+
'php': 'php',
|
|
383
|
+
'rb': 'ruby',
|
|
384
|
+
'go': 'go',
|
|
385
|
+
'rs': 'rust',
|
|
386
|
+
'json': 'json',
|
|
387
|
+
'xml': 'xml',
|
|
388
|
+
'html': 'html',
|
|
389
|
+
'css': 'css',
|
|
390
|
+
'scss': 'scss',
|
|
391
|
+
'yaml': 'yaml',
|
|
392
|
+
'yml': 'yaml',
|
|
393
|
+
'sql': 'sql',
|
|
394
|
+
'sh': 'bash',
|
|
395
|
+
'bash': 'bash',
|
|
396
|
+
'zsh': 'bash'
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
return extMap[ext?.toLowerCase()] || 'plaintext';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Truncate text with ellipsis
|
|
404
|
+
*/
|
|
405
|
+
truncateText(text, maxLength = 200) {
|
|
406
|
+
if (text.length <= maxLength) {
|
|
407
|
+
return text;
|
|
408
|
+
}
|
|
409
|
+
return text.substring(0, maxLength) + '...';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* HTML escape utility
|
|
414
|
+
*/
|
|
415
|
+
escapeHtml(text) {
|
|
416
|
+
return window._escHtml(text);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Format timestamp
|
|
421
|
+
*/
|
|
422
|
+
formatTimestamp(timestamp) {
|
|
423
|
+
const date = new Date(timestamp);
|
|
424
|
+
return date.toLocaleTimeString();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get statistics
|
|
429
|
+
*/
|
|
430
|
+
getStats() {
|
|
431
|
+
return { ...this.stats };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Reset statistics
|
|
436
|
+
*/
|
|
437
|
+
resetStats() {
|
|
438
|
+
this.stats = {
|
|
439
|
+
totalEvents: 0,
|
|
440
|
+
processedEvents: 0,
|
|
441
|
+
validatedEvents: 0,
|
|
442
|
+
transformedEvents: 0,
|
|
443
|
+
errorCount: 0,
|
|
444
|
+
avgProcessTime: 0
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Export for use in browser
|
|
450
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
451
|
+
module.exports = EventProcessor;
|
|
452
|
+
}
|