agentgui 1.0.67 → 1.0.68

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 (55) hide show
  1. package/.prd +214 -0
  2. package/.prd-browser +607 -0
  3. package/CLAUDE.md +1532 -125
  4. package/browser-test-harness.js +371 -0
  5. package/browser-test.js +409 -0
  6. package/execute-tests.js +164 -0
  7. package/lib/claude-runner.js +41 -12
  8. package/lib/database-service.ts +252 -0
  9. package/lib/sync-service.ts +275 -0
  10. package/lib/types.ts +168 -0
  11. package/package.json +1 -1
  12. package/readme.md +586 -0
  13. package/run-e2e-test.sh +88 -0
  14. package/server.js +274 -8
  15. package/static/index.html +487 -180
  16. package/static/js/client.js +558 -0
  17. package/static/js/event-filter.js +311 -0
  18. package/static/js/event-processor.js +454 -0
  19. package/static/js/streaming-renderer.js +813 -0
  20. package/static/js/syntax-highlighter.js +271 -0
  21. package/static/js/ui-components.js +433 -0
  22. package/static/js/websocket-manager.js +482 -0
  23. package/static/templates/INDEX.html +465 -0
  24. package/static/templates/README.md +190 -0
  25. package/static/templates/agent-capabilities.html +56 -0
  26. package/static/templates/agent-metadata-panel.html +44 -0
  27. package/static/templates/agent-status-badge.html +30 -0
  28. package/static/templates/code-annotation-panel.html +155 -0
  29. package/static/templates/code-suggestion-panel.html +184 -0
  30. package/static/templates/command-header.html +77 -0
  31. package/static/templates/command-output-scrollable.html +118 -0
  32. package/static/templates/elapsed-time.html +54 -0
  33. package/static/templates/error-alert.html +106 -0
  34. package/static/templates/error-history-timeline.html +160 -0
  35. package/static/templates/error-recovery-options.html +109 -0
  36. package/static/templates/error-stack-trace.html +95 -0
  37. package/static/templates/error-summary.html +80 -0
  38. package/static/templates/event-counter.html +48 -0
  39. package/static/templates/execution-actions.html +97 -0
  40. package/static/templates/execution-progress-bar.html +80 -0
  41. package/static/templates/execution-stepper.html +120 -0
  42. package/static/templates/file-breadcrumb.html +118 -0
  43. package/static/templates/file-diff-viewer.html +121 -0
  44. package/static/templates/file-metadata.html +133 -0
  45. package/static/templates/file-read-panel.html +66 -0
  46. package/static/templates/file-write-panel.html +120 -0
  47. package/static/templates/git-branch-remote.html +107 -0
  48. package/static/templates/git-diff-list.html +101 -0
  49. package/static/templates/git-log-visualization.html +153 -0
  50. package/static/templates/git-status-panel.html +115 -0
  51. package/static/templates/quality-metrics-display.html +170 -0
  52. package/static/templates/terminal-output-panel.html +87 -0
  53. package/static/templates/test-results-display.html +144 -0
  54. package/test-browser.js +457 -0
  55. package/test-runner.js +182 -0
@@ -0,0 +1,454 @@
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
+ const div = document.createElement('div');
417
+ div.textContent = text;
418
+ return div.innerHTML;
419
+ }
420
+
421
+ /**
422
+ * Format timestamp
423
+ */
424
+ formatTimestamp(timestamp) {
425
+ const date = new Date(timestamp);
426
+ return date.toLocaleTimeString();
427
+ }
428
+
429
+ /**
430
+ * Get statistics
431
+ */
432
+ getStats() {
433
+ return { ...this.stats };
434
+ }
435
+
436
+ /**
437
+ * Reset statistics
438
+ */
439
+ resetStats() {
440
+ this.stats = {
441
+ totalEvents: 0,
442
+ processedEvents: 0,
443
+ validatedEvents: 0,
444
+ transformedEvents: 0,
445
+ errorCount: 0,
446
+ avgProcessTime: 0
447
+ };
448
+ }
449
+ }
450
+
451
+ // Export for use in browser
452
+ if (typeof module !== 'undefined' && module.exports) {
453
+ module.exports = EventProcessor;
454
+ }