@auto-engineer/cli 0.9.7 → 0.9.8

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 (35) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/src/dsl/index.d.ts +5 -1
  3. package/dist/src/dsl/index.d.ts.map +1 -1
  4. package/dist/src/dsl/index.js +6 -0
  5. package/dist/src/dsl/index.js.map +1 -1
  6. package/dist/src/dsl/types.d.ts +6 -0
  7. package/dist/src/dsl/types.d.ts.map +1 -1
  8. package/dist/src/dsl-exports.d.ts +2 -2
  9. package/dist/src/dsl-exports.d.ts.map +1 -1
  10. package/dist/src/dsl-exports.js +1 -1
  11. package/dist/src/dsl-exports.js.map +1 -1
  12. package/dist/src/index.d.ts +1 -1
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/index.js +8 -8
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/plugin-loader.d.ts +1 -0
  17. package/dist/src/plugin-loader.d.ts.map +1 -1
  18. package/dist/src/plugin-loader.js.map +1 -1
  19. package/dist/src/server/config-loader.d.ts +2 -17
  20. package/dist/src/server/config-loader.d.ts.map +1 -1
  21. package/dist/src/server/config-loader.js +16 -72
  22. package/dist/src/server/config-loader.js.map +1 -1
  23. package/dist/src/server/file-syncer/discovery/dts.js +1 -1
  24. package/dist/src/server/file-syncer/index.js +3 -3
  25. package/dist/src/server/file-syncer/sync/resolveSyncFileSet.js +3 -3
  26. package/dist/src/server/http-routes.d.ts.map +1 -1
  27. package/dist/src/server/http-routes.js +2 -6
  28. package/dist/src/server/http-routes.js.map +1 -1
  29. package/dist/src/server/server.js +6 -6
  30. package/dist/src/utils/analytics.js +1 -1
  31. package/dist/src/utils/config.js +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +5 -5
  34. package/dist/src/server/canvas-landing.html +0 -291
  35. package/dist/src/server/dashboard.html +0 -1774
@@ -1,1774 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Auto Engineer</title>
7
- <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg width='637' height='637' viewBox='0 0 637 637' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_1042_512)'%3E%3Crect width='637' height='637'/%3E%3Cpath d='M693.161 -427.743L-623.632 1532.31L-591.107 1552.06L725.686 -407.988L693.161 -427.743Z' fill='%23EC3F4A'/%3E%3Cpath d='M725.695 -407.962L-591.097 1552.09L-571.511 1563.98L745.281 -396.066L725.695 -407.962Z' fill='%231A1A1A'/%3E%3Cpath d='M745.281 -396.071L-571.511 1563.98L-538.986 1583.73L777.807 -376.315L745.281 -396.071Z' fill='%23FF8A1D'/%3E%3Cpath d='M777.815 -376.302L-538.978 1583.75L-519.391 1595.64L797.402 -364.406L777.815 -376.302Z' fill='%231A1A1A'/%3E%3Cpath d='M797.394 -364.425L-519.399 1595.62L-486.874 1615.38L829.919 -344.67L797.394 -364.425Z' fill='%235EC72D'/%3E%3Cpath d='M829.913 -344.684L-486.879 1615.37L-467.293 1627.26L849.499 -332.787L829.913 -344.684Z' fill='%231A1A1A'/%3E%3Cpath d='M849.467 -332.791L-467.326 1627.26L-434.801 1647.01L881.992 -313.035L849.467 -332.791Z' fill='%2342C3F7'/%3E%3Cpath d='M882 -313.038L-434.792 1647.01L-415.206 1658.91L901.587 -301.142L882 -313.038Z' fill='%231A1A1A'/%3E%3Cpath d='M673.55 -439.635L-643.242 1520.42L-623.656 1532.31L693.136 -427.738L673.55 -439.635Z' fill='%231A1A1A'/%3E%3Cpath d='M512.644 100H121.718C109.652 100 100 109.667 100 121.751V513.277C100 525.361 109.652 535.028 121.718 535.028H512.644C524.71 535.028 534.362 525.361 534.362 513.277V121.751C534.362 109.667 524.71 100 512.644 100Z' fill='white'/%3E%3Cpath d='M413.706 481.86C451.11 481.86 481.274 451.649 481.274 414.189C481.274 376.728 451.11 346.518 413.706 346.518C376.303 346.518 346.139 376.728 346.139 414.189C346.139 451.649 376.303 481.86 413.706 481.86Z' fill='%231A1A1A'/%3E%3Cpath d='M153.089 347.968C153.089 347.243 153.813 346.518 154.537 346.518H287.017C287.741 346.518 288.465 347.243 288.465 347.968V412.98C288.465 413.705 287.741 414.43 287.017 414.43H257.095C256.371 414.43 255.647 415.155 255.647 415.88V480.893C255.647 481.618 254.923 482.343 254.199 482.343H188.321C187.597 482.343 186.873 481.618 186.873 480.893V415.88C186.873 415.155 186.149 414.43 185.425 414.43H154.296C153.572 414.43 152.848 413.705 152.848 412.98V347.968H153.089Z' fill='%231A1A1A'/%3E%3Cpath d='M347.587 153.17C346.863 153.17 346.139 153.895 346.139 154.621V223.017C346.139 259.269 376.303 288.513 413.706 288.513C451.11 288.513 481.274 259.269 481.274 223.017V154.621C481.274 153.895 480.55 153.17 479.826 153.17H347.345H347.587Z' fill='%231A1A1A'/%3E%3Cpath d='M153.33 286.339C152.848 287.306 153.33 288.272 154.537 288.272H286.776C287.742 288.272 288.465 287.306 287.983 286.339L221.863 154.139C221.381 153.172 219.933 153.172 219.45 154.139L153.33 286.339Z' fill='%231A1A1A'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_1042_512'%3E%3Crect width='637' height='637' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E" />
8
- <style>
9
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
10
-
11
- * {
12
- margin: 0;
13
- padding: 0;
14
- box-sizing: border-box;
15
- }
16
-
17
- :root {
18
- /* Auto Brand Colors */
19
- --brand-red: #ec3f4a;
20
- --brand-orange: #ff8a1d;
21
- --brand-green: #5ec72d;
22
- --brand-blue: #42c3f7;
23
- --brand-primary: #0057dd;
24
- }
25
-
26
- /* Dark theme (default) */
27
- [data-theme="dark"] {
28
- --bg-primary: #1a1a1a;
29
- --bg-secondary: #18181b;
30
- --bg-tertiary: #27272a;
31
- --bg-hover: #333337;
32
- --border: #3a3a3d;
33
- --border-light: #4a4a4d;
34
- --text-primary: #ffffff;
35
- --text-secondary: #a1a1aa;
36
- --text-tertiary: #71717a;
37
- --accent: #0057dd;
38
- --accent-hover: #0046b5;
39
- --success: #5ec72d;
40
- --error: #ec3f4a;
41
- --warning: #ff8a1d;
42
- --code-bg: #0d0e11;
43
- }
44
-
45
- /* Light theme */
46
- [data-theme="light"] {
47
- --bg-primary: #fafafa;
48
- --bg-secondary: #ffffff;
49
- --bg-tertiary: #f4f4f5;
50
- --bg-hover: #e4e4e7;
51
- --border: #e4e4e7;
52
- --border-light: #d4d4d8;
53
- --text-primary: #18181b;
54
- --text-secondary: #52525b;
55
- --text-tertiary: #71717a;
56
- --accent: #0057dd;
57
- --accent-hover: #0046b5;
58
- --success: #5ec72d;
59
- --error: #ec3f4a;
60
- --warning: #ff8a1d;
61
- --code-bg: #f4f4f5;
62
- }
63
-
64
- body {
65
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
66
- background: var(--bg-primary);
67
- color: var(--text-primary);
68
- min-height: 100vh;
69
- font-size: 14px;
70
- line-height: 1.6;
71
- -webkit-font-smoothing: antialiased;
72
- -moz-osx-font-smoothing: grayscale;
73
- transition: background-color 0.3s ease, color 0.3s ease;
74
- }
75
-
76
- /* Header */
77
- .header {
78
- background: var(--bg-secondary);
79
- border-bottom: 1px solid var(--border);
80
- padding: 0 24px;
81
- height: 56px;
82
- display: flex;
83
- align-items: center;
84
- justify-content: space-between;
85
- position: relative;
86
- transition: background-color 0.3s ease;
87
- }
88
-
89
- .header-logo {
90
- display: flex;
91
- align-items: center;
92
- gap: 12px;
93
- }
94
-
95
- .logo-mark {
96
- width: 40px;
97
- height: 40px;
98
- display: flex;
99
- align-items: center;
100
- justify-content: center;
101
- }
102
-
103
- .logo-mark svg {
104
- width: 40px;
105
- height: 40px;
106
- }
107
-
108
- .logo-text {
109
- font-size: 18px;
110
- font-weight: 600;
111
- color: var(--text-primary);
112
- letter-spacing: -0.02em;
113
- }
114
-
115
- .header-controls {
116
- display: flex;
117
- align-items: center;
118
- gap: 24px;
119
- }
120
-
121
- /* Theme switcher dropdown */
122
- .theme-switcher {
123
- position: relative;
124
- }
125
-
126
- .theme-toggle {
127
- display: flex;
128
- align-items: center;
129
- justify-content: center;
130
- width: 36px;
131
- height: 36px;
132
- background: var(--bg-tertiary);
133
- border: 1px solid var(--border);
134
- border-radius: 8px;
135
- cursor: pointer;
136
- transition: all 0.2s ease;
137
- padding: 0;
138
- color: var(--text-secondary);
139
- }
140
-
141
- .theme-toggle:hover {
142
- background: var(--bg-hover);
143
- border-color: var(--border-light);
144
- }
145
-
146
- .theme-toggle:focus {
147
- outline: none;
148
- border-color: var(--accent);
149
- box-shadow: 0 0 0 3px rgba(0, 87, 221, 0.1);
150
- }
151
-
152
- .theme-dropdown {
153
- position: absolute;
154
- top: calc(100% + 4px);
155
- right: 0;
156
- background: var(--bg-secondary);
157
- border: 1px solid var(--border);
158
- border-radius: 8px;
159
- padding: 4px;
160
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
161
- opacity: 0;
162
- visibility: hidden;
163
- transform: translateY(-4px);
164
- transition: all 0.15s ease;
165
- z-index: 1000;
166
- min-width: 140px;
167
- }
168
-
169
- .theme-dropdown.open {
170
- opacity: 1;
171
- visibility: visible;
172
- transform: translateY(0);
173
- }
174
-
175
- .theme-option {
176
- display: flex;
177
- align-items: center;
178
- gap: 10px;
179
- padding: 8px 12px;
180
- border: none;
181
- background: transparent;
182
- color: var(--text-primary) !important;
183
- font-size: 12px;
184
- font-weight: 500;
185
- cursor: pointer;
186
- border-radius: 6px;
187
- transition: all 0.15s ease;
188
- width: 100%;
189
- text-align: left;
190
- }
191
-
192
- .theme-option:hover {
193
- background: var(--bg-tertiary);
194
- color: var(--text-primary) !important;
195
- }
196
-
197
- .theme-option.active {
198
- background: var(--bg-tertiary);
199
- color: var(--text-primary) !important;
200
- }
201
-
202
- .theme-icon {
203
- width: 16px;
204
- height: 16px;
205
- flex-shrink: 0;
206
- color: var(--text-secondary);
207
- }
208
-
209
- /* Main layout - split into top and bottom sections */
210
- .app-layout {
211
- display: flex;
212
- flex-direction: column;
213
- height: calc(100vh - 56px);
214
- }
215
-
216
- /* Top section - Command interface (low profile) */
217
- .command-section {
218
- background: var(--bg-secondary);
219
- padding: 16px 24px;
220
- display: flex;
221
- flex-direction: column;
222
- gap: 12px;
223
- min-height: 60px;
224
- }
225
-
226
- /* Command controls row */
227
- .command-controls {
228
- display: flex;
229
- gap: 12px;
230
- align-items: center;
231
- }
232
-
233
- .command-dropdown {
234
- position: relative;
235
- min-width: 320px;
236
- }
237
-
238
- .command-trigger {
239
- width: 100%;
240
- padding: 8px 36px 8px 12px;
241
- background: var(--bg-tertiary);
242
- border: 1px solid var(--border);
243
- border-radius: 6px;
244
- color: var(--text-primary);
245
- font-size: 13px;
246
- font-weight: 500;
247
- cursor: pointer;
248
- transition: all 0.15s ease;
249
- display: flex;
250
- align-items: center;
251
- justify-content: space-between;
252
- min-height: 36px;
253
- }
254
-
255
- .command-trigger:hover {
256
- background: var(--bg-hover);
257
- border-color: var(--border-light);
258
- }
259
-
260
- .command-trigger.open {
261
- border-color: var(--accent);
262
- box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
263
- }
264
-
265
- .command-trigger-text {
266
- color: var(--text-secondary);
267
- }
268
-
269
- .command-trigger.selected .command-trigger-text {
270
- color: var(--text-primary);
271
- font-weight: 600;
272
- }
273
-
274
- .command-dropdown-icon {
275
- color: var(--text-tertiary);
276
- font-size: 10px;
277
- transition: transform 0.15s ease;
278
- }
279
-
280
- .command-trigger.open .command-dropdown-icon {
281
- transform: rotate(180deg);
282
- }
283
-
284
- .command-options {
285
- position: absolute;
286
- top: calc(100% + 4px);
287
- left: 0;
288
- right: 0;
289
- background: var(--bg-secondary);
290
- border: 1px solid var(--border);
291
- border-radius: 8px;
292
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
293
- opacity: 0;
294
- visibility: hidden;
295
- transform: translateY(-4px);
296
- transition: all 0.15s ease;
297
- z-index: 1000;
298
- max-height: 300px;
299
- overflow-y: auto;
300
- padding: 4px;
301
- }
302
-
303
- .command-options.open {
304
- opacity: 1;
305
- visibility: visible;
306
- transform: translateY(0);
307
- }
308
-
309
- .command-option {
310
- display: flex;
311
- flex-direction: column;
312
- padding: 12px 12px 12px 20px;
313
- margin: 2px 0;
314
- background: transparent;
315
- border: none;
316
- border-radius: 6px;
317
- cursor: pointer;
318
- transition: all 0.15s ease;
319
- width: 100%;
320
- text-align: left;
321
- gap: 2px;
322
- position: relative;
323
- }
324
-
325
- .command-option::before {
326
- content: '';
327
- position: absolute;
328
- left: 4px;
329
- top: 50%;
330
- transform: translateY(-50%);
331
- width: 6px;
332
- height: 6px;
333
- background: #42c3f7;
334
- border-radius: 2px;
335
- }
336
-
337
- .command-option:hover {
338
- background: var(--bg-hover);
339
- }
340
-
341
- .command-option.selected {
342
- background: var(--bg-tertiary);
343
- }
344
-
345
- .command-option-name {
346
- font-size: 13px;
347
- font-weight: 600;
348
- color: var(--text-primary);
349
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
350
- }
351
-
352
- .command-option-org {
353
- font-size: 11px;
354
- color: var(--text-secondary);
355
- font-weight: 500;
356
- }
357
-
358
- .command-option-package {
359
- font-size: 11px;
360
- color: var(--text-tertiary);
361
- font-weight: 400;
362
- }
363
-
364
- .request-id-input {
365
- padding: 8px 12px;
366
- background: var(--bg-tertiary);
367
- border: 1px solid var(--border);
368
- border-radius: 6px;
369
- color: var(--text-primary);
370
- font-size: 12px;
371
- width: 200px;
372
- height: 36px;
373
- box-sizing: border-box;
374
- transition: all 0.15s ease;
375
- }
376
-
377
- .request-id-input:focus {
378
- outline: none;
379
- border-color: var(--accent);
380
- background: var(--bg-secondary);
381
- box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
382
- }
383
-
384
- .send-button {
385
- padding: 8px 20px;
386
- background: var(--accent);
387
- color: white;
388
- border: none;
389
- border-radius: 6px;
390
- font-size: 12px;
391
- font-weight: 600;
392
- cursor: pointer;
393
- height: 36px;
394
- box-sizing: border-box;
395
- transition: all 0.15s ease;
396
- text-transform: uppercase;
397
- letter-spacing: 0.05em;
398
- }
399
-
400
- .send-button:hover:not(:disabled) {
401
- background: var(--accent-hover);
402
- transform: translateY(-1px);
403
- box-shadow: 0 2px 8px rgba(0, 87, 221, 0.3);
404
- }
405
-
406
- .send-button:disabled {
407
- opacity: 0.5;
408
- cursor: not-allowed;
409
- }
410
-
411
- .response-status {
412
- display: flex;
413
- align-items: center;
414
- gap: 8px;
415
- font-size: 11px;
416
- font-weight: 600;
417
- text-transform: uppercase;
418
- letter-spacing: 0.05em;
419
- }
420
-
421
- .status-dot {
422
- width: 6px;
423
- height: 6px;
424
- border-radius: 50%;
425
- background: var(--text-tertiary);
426
- }
427
-
428
- .status-dot.success {
429
- background: var(--success);
430
- box-shadow: 0 0 6px rgba(94, 199, 45, 0.5);
431
- }
432
-
433
- .status-dot.error {
434
- background: var(--error);
435
- box-shadow: 0 0 6px rgba(236, 63, 74, 0.5);
436
- }
437
-
438
- .request-body {
439
- flex: 1;
440
- height: 36px;
441
- min-height: 36px;
442
- max-height: 120px;
443
- padding: 8px 12px;
444
- background: var(--bg-tertiary);
445
- border: 1px solid var(--border);
446
- border-radius: 6px;
447
- color: var(--text-primary);
448
- font-size: 12px;
449
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
450
- resize: vertical;
451
- box-sizing: border-box;
452
- transition: all 0.15s ease;
453
- }
454
-
455
- .request-body:focus {
456
- outline: none;
457
- border-color: var(--accent);
458
- background: var(--bg-secondary);
459
- box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
460
- }
461
-
462
-
463
- /* Bottom section - Messages list */
464
- .messages-section {
465
- flex: 1;
466
- display: flex;
467
- flex-direction: column;
468
- overflow: hidden;
469
- margin-top: 24px;
470
- border-top: 2px solid var(--border);
471
- }
472
-
473
- .messages-header {
474
- padding: 18px 24px;
475
- background: var(--bg-primary);
476
- border-bottom: 1px solid var(--border);
477
- display: flex;
478
- justify-content: space-between;
479
- align-items: center;
480
- }
481
-
482
- .messages-title {
483
- font-size: 14px;
484
- font-weight: 700;
485
- text-transform: uppercase;
486
- letter-spacing: 0.05em;
487
- color: var(--text-primary);
488
- display: flex;
489
- align-items: center;
490
- gap: 12px;
491
- }
492
-
493
- .messages-title span:last-child {
494
- background: var(--accent);
495
- color: var(--bg-primary);
496
- padding: 4px 10px;
497
- border-radius: 16px;
498
- font-size: 11px;
499
- font-weight: 600;
500
- text-transform: none;
501
- letter-spacing: normal;
502
- }
503
-
504
- .messages-controls {
505
- display: flex;
506
- gap: 8px;
507
- }
508
-
509
- .button-small {
510
- padding: 6px 12px;
511
- background: var(--bg-secondary);
512
- border: 1px solid var(--border);
513
- border-radius: 4px;
514
- font-size: 11px;
515
- font-weight: 500;
516
- color: var(--text-primary);
517
- cursor: pointer;
518
- transition: all 0.15s ease;
519
- }
520
-
521
- .button-small:hover {
522
- background: var(--bg-hover);
523
- border-color: var(--border-light);
524
- }
525
-
526
- /* Message filters */
527
- .message-filters {
528
- padding: 16px 24px;
529
- background: var(--bg-tertiary);
530
- border-bottom: 2px solid var(--border);
531
- display: flex;
532
- gap: 16px;
533
- flex-wrap: wrap;
534
- align-items: center;
535
- }
536
-
537
- .filter-control {
538
- padding: 6px 8px;
539
- border: 1px solid var(--border);
540
- border-radius: 4px;
541
- background: var(--bg-primary);
542
- color: var(--text-primary);
543
- font-size: 11px;
544
- min-width: 120px;
545
- }
546
-
547
- .filter-stats {
548
- font-size: 10px;
549
- color: var(--text-tertiary);
550
- margin-left: auto;
551
- }
552
-
553
- /* Messages list */
554
- .messages-list {
555
- flex: 1;
556
- overflow-y: auto;
557
- display: flex;
558
- flex-direction: column;
559
- }
560
-
561
- .messages-table {
562
- width: 100%;
563
- border-collapse: separate;
564
- border-spacing: 0;
565
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
566
- font-size: 11px;
567
- table-layout: fixed;
568
- min-width: 800px;
569
- }
570
-
571
- .messages-header-row {
572
- background: var(--bg-tertiary);
573
- border-bottom: 2px solid var(--border);
574
- position: sticky;
575
- top: 0;
576
- z-index: 10;
577
- }
578
-
579
- .messages-header-cell {
580
- padding: 8px 6px;
581
- text-align: left;
582
- font-weight: 600;
583
- font-size: 10px;
584
- text-transform: uppercase;
585
- letter-spacing: 0.05em;
586
- color: var(--text-secondary);
587
- border-right: 1px solid var(--border);
588
- position: relative;
589
- user-select: none;
590
- box-sizing: border-box;
591
- overflow: hidden;
592
- }
593
-
594
- .messages-header-cell:last-child {
595
- border-right: none;
596
- }
597
-
598
-
599
- .message-details-row {
600
- background: var(--code-bg);
601
- }
602
-
603
- .message-details {
604
- padding: 16px;
605
- background: var(--code-bg);
606
- border-top: 1px solid var(--border);
607
- font-family: 'SF Mono', 'Monaco', monospace;
608
- }
609
-
610
- .message-data {
611
- background: var(--bg-primary);
612
- border: 1px solid var(--border);
613
- border-radius: 4px;
614
- padding: 12px;
615
- font-size: 11px;
616
- line-height: 1.4;
617
- color: var(--text-primary);
618
- max-height: 300px;
619
- overflow-y: auto;
620
- white-space: pre-wrap;
621
- word-break: break-word;
622
- position: relative;
623
- }
624
-
625
- .copy-message-btn {
626
- position: absolute;
627
- top: 8px;
628
- right: 8px;
629
- padding: 4px 8px;
630
- background: var(--bg-secondary);
631
- border: 1px solid var(--border);
632
- border-radius: 4px;
633
- font-size: 10px;
634
- color: var(--text-secondary);
635
- cursor: pointer;
636
- transition: all 0.15s ease;
637
- }
638
-
639
- .copy-message-btn:hover {
640
- background: var(--bg-hover);
641
- color: var(--text-primary);
642
- }
643
-
644
- .message-row {
645
- background: var(--bg-secondary);
646
- border-bottom: 1px solid var(--border);
647
- transition: background-color 0.15s ease;
648
- }
649
-
650
- .message-row:hover {
651
- background: var(--bg-hover);
652
- }
653
-
654
- .message-row.expanded {
655
- background: var(--bg-tertiary);
656
- }
657
-
658
- .message-cell {
659
- padding: 4px 6px;
660
- border-right: 1px solid var(--border);
661
- vertical-align: top;
662
- font-size: 11px;
663
- box-sizing: border-box;
664
- overflow: hidden;
665
- word-wrap: break-word;
666
- line-height: 1.3;
667
- }
668
-
669
- .message-cell:last-child {
670
- border-right: none;
671
- }
672
-
673
- .expand-cell {
674
- width: 30px;
675
- text-align: center;
676
- cursor: pointer;
677
- user-select: none;
678
- }
679
-
680
- .expand-btn {
681
- width: 16px;
682
- height: 16px;
683
- display: inline-flex;
684
- align-items: center;
685
- justify-content: center;
686
- background: var(--bg-primary);
687
- border: 1px solid var(--border);
688
- border-radius: 3px;
689
- font-size: 9px;
690
- font-weight: 600;
691
- color: var(--text-secondary);
692
- transition: all 0.15s ease;
693
- }
694
-
695
- .expand-btn:hover {
696
- background: var(--bg-hover);
697
- color: var(--text-primary);
698
- }
699
-
700
- .type-cell {
701
- width: 80px;
702
- text-align: center;
703
- }
704
-
705
- .message-type {
706
- font-size: 9px;
707
- font-weight: 600;
708
- text-transform: uppercase;
709
- letter-spacing: 0.05em;
710
- padding: 2px 6px;
711
- border-radius: 3px;
712
- display: inline-block;
713
- min-width: 60px;
714
- text-align: center;
715
- }
716
-
717
- .message-type.command {
718
- color: #42c3f7;
719
- background: rgba(66, 195, 247, 0.1);
720
- }
721
-
722
- .message-type.event {
723
- color: #ff8a1d;
724
- background: rgba(255, 138, 29, 0.1);
725
- }
726
-
727
- .name-cell {
728
- min-width: 120px;
729
- max-width: 150px;
730
- }
731
-
732
- .message-name {
733
- font-weight: 600;
734
- color: var(--text-primary);
735
- }
736
-
737
- .id-cell {
738
- width: 100px;
739
- font-size: 10px;
740
- color: var(--text-tertiary);
741
- }
742
-
743
- .position-cell {
744
- width: 60px;
745
- text-align: center;
746
- font-size: 10px;
747
- color: var(--text-secondary);
748
- }
749
-
750
- .time-cell {
751
- width: 80px;
752
- text-align: right;
753
- font-size: 10px;
754
- color: var(--text-tertiary);
755
- }
756
-
757
- .message-type-icon {
758
- width: 12px;
759
- height: 12px;
760
- display: flex;
761
- align-items: center;
762
- justify-content: center;
763
- font-size: 8px;
764
- flex-shrink: 0;
765
- }
766
-
767
- .message-type {
768
- min-width: 50px;
769
- font-size: 10px;
770
- font-weight: 600;
771
- text-transform: uppercase;
772
- letter-spacing: 0.05em;
773
- flex-shrink: 0;
774
- }
775
-
776
- .message-type.command {
777
- color: #42c3f7;
778
- }
779
-
780
- .message-type.event {
781
- color: #ff8a1d;
782
- }
783
-
784
- .message-name {
785
- min-width: 120px;
786
- font-weight: 500;
787
- color: var(--text-primary);
788
- flex-shrink: 0;
789
- }
790
-
791
- .message-meta {
792
- display: flex;
793
- gap: 12px;
794
- align-items: center;
795
- font-size: 10px;
796
- color: var(--text-tertiary);
797
- flex: 1;
798
- }
799
-
800
- .message-timestamp {
801
- font-size: 10px;
802
- color: var(--text-tertiary);
803
- min-width: 80px;
804
- text-align: right;
805
- flex-shrink: 0;
806
- }
807
-
808
- .message-details {
809
- display: none;
810
- padding: 16px;
811
- background: var(--code-bg);
812
- border-top: 1px solid var(--border);
813
- }
814
-
815
- .message-item.expanded .message-details {
816
- display: block;
817
- }
818
-
819
- .message-ids {
820
- display: grid;
821
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
822
- gap: 12px;
823
- margin-bottom: 12px;
824
- font-size: 10px;
825
- color: var(--text-tertiary);
826
- font-family: 'SF Mono', 'Monaco', monospace;
827
- }
828
-
829
- .message-id {
830
- display: flex;
831
- flex-direction: column;
832
- gap: 2px;
833
- }
834
-
835
- .message-id-label {
836
- color: var(--text-secondary);
837
- font-weight: 600;
838
- text-transform: uppercase;
839
- letter-spacing: 0.05em;
840
- }
841
-
842
- .message-data {
843
- background: var(--bg-primary);
844
- border: 1px solid var(--border);
845
- border-radius: 4px;
846
- padding: 12px;
847
- font-family: 'SF Mono', 'Monaco', monospace;
848
- font-size: 11px;
849
- line-height: 1.4;
850
- color: var(--text-primary);
851
- max-height: 300px;
852
- overflow-y: auto;
853
- white-space: pre-wrap;
854
- word-break: break-word;
855
- }
856
-
857
- .empty {
858
- padding: 32px;
859
- text-align: center;
860
- color: var(--text-tertiary);
861
- font-size: 12px;
862
- }
863
-
864
- /* Scrollbar styling */
865
- ::-webkit-scrollbar {
866
- width: 8px;
867
- height: 8px;
868
- }
869
-
870
- ::-webkit-scrollbar-track {
871
- background: var(--bg-secondary);
872
- }
873
-
874
- ::-webkit-scrollbar-thumb {
875
- background: var(--border-light);
876
- border-radius: 4px;
877
- }
878
-
879
- ::-webkit-scrollbar-thumb:hover {
880
- background: var(--text-tertiary);
881
- }
882
- </style>
883
- </head>
884
- <body data-theme="dark">
885
- <div class="header">
886
- <div class="header-logo">
887
- <div class="logo-mark" id="logoMark">
888
- </div>
889
- <div>
890
- <span class="logo-text">auto engineer</span>
891
- </div>
892
- </div>
893
- <div class="header-controls">
894
- <div class="theme-switcher">
895
- <button class="theme-toggle" id="themeToggle" onclick="toggleThemeDropdown()">
896
- <svg class="theme-icon" id="currentThemeIcon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
897
- <!-- Icon will be set based on current theme -->
898
- </svg>
899
- </button>
900
- <div class="theme-dropdown" id="themeDropdown">
901
- <button class="theme-option" onclick="setTheme('light')">
902
- <svg class="theme-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
903
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
904
- </svg>
905
- <span>Light</span>
906
- </button>
907
- <button class="theme-option" onclick="setTheme('dark')">
908
- <svg class="theme-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
909
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
910
- </svg>
911
- <span>Dark</span>
912
- </button>
913
- <button class="theme-option" onclick="setTheme('system')">
914
- <svg class="theme-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
915
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
916
- </svg>
917
- <span>System</span>
918
- </button>
919
- </div>
920
- </div>
921
- </div>
922
- </div>
923
-
924
- <div class="app-layout">
925
- <!-- Top section: Command interface -->
926
- <div class="command-section">
927
- <!-- Command controls row -->
928
- <div class="command-controls">
929
- <div class="command-dropdown">
930
- <div class="command-trigger" id="commandTrigger" onclick="toggleCommandDropdown()">
931
- <span class="command-trigger-text">Select a command...</span>
932
- <span class="command-dropdown-icon">▼</span>
933
- </div>
934
- <div class="command-options" id="commandOptions">
935
- <!-- Command options will be populated by JavaScript -->
936
- </div>
937
- </div>
938
-
939
- <textarea class="request-body" id="requestBody" placeholder='Request body (JSON)&#10;Example: {"key": "value"}'>{}</textarea>
940
-
941
- <input type="text" class="request-id-input" id="requestIdInput" placeholder="Request ID (optional)" />
942
-
943
- <button class="send-button" onclick="sendRequest()" id="sendButton" disabled>
944
- Send
945
- </button>
946
-
947
- <div class="response-status">
948
- <div class="status-dot" id="statusDot"></div>
949
- <span id="statusText">Ready</span>
950
- <span id="responseTime"></span>
951
- </div>
952
- </div>
953
- </div>
954
-
955
- <!-- Bottom section: Messages list -->
956
- <div class="messages-section">
957
- <div class="messages-header">
958
- <div class="messages-title">
959
- <span>Messages</span>
960
- <span id="messageCount">0</span>
961
- </div>
962
- <div class="messages-controls">
963
- <button class="button-small" onclick="clearFilters()">Clear Filters</button>
964
- </div>
965
- </div>
966
-
967
- <div class="message-filters">
968
- <select class="filter-control" id="messageTypeFilter" onchange="applyFilters()">
969
- <option value="">All Types</option>
970
- <option value="command">Commands</option>
971
- <option value="event">Events</option>
972
- </select>
973
-
974
- <select class="filter-control" id="sessionFilter" onchange="applyFilters()">
975
- <option value="">All Sessions</option>
976
- </select>
977
-
978
- <input type="text" class="filter-control" id="messageNameFilter" placeholder="Message name..." onchange="applyFilters()" />
979
-
980
- <input type="text" class="filter-control" id="correlationIdFilter" placeholder="Correlation ID..." onchange="applyFilters()" />
981
-
982
- <div class="filter-stats" id="filterStats">
983
- Showing all messages
984
- </div>
985
- </div>
986
-
987
- <div class="messages-list" id="messagesList">
988
- <table class="messages-table">
989
- <colgroup>
990
- <col style="width: 40px;">
991
- <col style="width: 80px;">
992
- <col style="width: 180px;">
993
- <col style="width: 120px;">
994
- <col style="width: 120px;">
995
- <col style="width: 120px;">
996
- <col style="width: 60px;">
997
- <col style="width: 90px;">
998
- </colgroup>
999
- <thead>
1000
- <tr class="messages-header-row">
1001
- <th class="messages-header-cell expand-cell" data-column="expand">
1002
- </th>
1003
- <th class="messages-header-cell type-cell" data-column="type">
1004
- Type
1005
- </th>
1006
- <th class="messages-header-cell name-cell" data-column="name">
1007
- Message
1008
- </th>
1009
- <th class="messages-header-cell id-cell" data-column="requestId">
1010
- Request ID
1011
- </th>
1012
- <th class="messages-header-cell id-cell" data-column="correlationId">
1013
- Correlation ID
1014
- </th>
1015
- <th class="messages-header-cell id-cell" data-column="sessionId">
1016
- Session ID
1017
- </th>
1018
- <th class="messages-header-cell position-cell" data-column="position">
1019
- Pos
1020
- </th>
1021
- <th class="messages-header-cell time-cell" data-column="time">
1022
- Time
1023
- </th>
1024
- </tr>
1025
- </thead>
1026
- <tbody id="messagesTableBody">
1027
- <tr><td colspan="8" style="text-align: center; padding: 32px; color: var(--text-tertiary);">No messages yet</td></tr>
1028
- </tbody>
1029
- </table>
1030
- </div>
1031
- </div>
1032
- </div>
1033
-
1034
- <script>
1035
- const API_BASE = window.location.origin;
1036
- let availableCommands = [];
1037
- let currentMessages = [];
1038
- let filteredMessages = [];
1039
- let availableSessions = [];
1040
- let currentCommand = null;
1041
- let responseStartTime;
1042
- let expandedMessages = new Set();
1043
- let isResizing = false;
1044
- let currentColumn = null;
1045
- let startX = 0;
1046
- let startWidth = 0;
1047
-
1048
- // Default column widths (percentages for better responsive behavior)
1049
- const defaultColumnWidths = {
1050
- expand: 40,
1051
- type: 80,
1052
- name: 180,
1053
- requestId: 120,
1054
- correlationId: 120,
1055
- sessionId: 120,
1056
- position: 60,
1057
- time: 90
1058
- };
1059
-
1060
- let columnWidths = { ...defaultColumnWidths };
1061
-
1062
- // Logo SVGs
1063
- const logoLight = `<svg width="637" height="637" viewBox="0 0 637 637" fill="none" xmlns="http://www.w3.org/2000/svg">
1064
- <g clip-path="url(#clip0_1042_573)">
1065
- <rect width="637" height="637"/>
1066
- <path d="M693.161 -427.743L-623.632 1532.31L-591.107 1552.06L725.686 -407.988L693.161 -427.743Z" fill="#EC3F4A"/>
1067
- <path d="M725.695 -407.962L-591.097 1552.09L-571.511 1563.98L745.281 -396.066L725.695 -407.962Z" fill="white"/>
1068
- <path d="M745.281 -396.071L-571.511 1563.98L-538.986 1583.73L777.807 -376.315L745.281 -396.071Z" fill="#FF8A1D"/>
1069
- <path d="M777.815 -376.302L-538.978 1583.75L-519.391 1595.64L797.402 -364.406L777.815 -376.302Z" fill="white"/>
1070
- <path d="M797.394 -364.425L-519.399 1595.62L-486.874 1615.38L829.919 -344.67L797.394 -364.425Z" fill="#5EC72D"/>
1071
- <path d="M829.913 -344.684L-486.879 1615.37L-467.293 1627.26L849.499 -332.787L829.913 -344.684Z" fill="white"/>
1072
- <path d="M849.467 -332.791L-467.326 1627.26L-434.801 1647.01L881.992 -313.035L849.467 -332.791Z" fill="#42C3F7"/>
1073
- <path d="M882 -313.038L-434.792 1647.01L-415.206 1658.91L901.587 -301.142L882 -313.038Z" fill="white"/>
1074
- <path d="M673.55 -439.635L-643.242 1520.42L-623.656 1532.31L693.136 -427.738L673.55 -439.635Z" fill="white"/>
1075
- <path d="M512.644 100H121.718C109.652 100 100 109.667 100 121.751V513.277C100 525.361 109.652 535.028 121.718 535.028H512.644C524.71 535.028 534.362 525.361 534.362 513.277V121.751C534.362 109.667 524.71 100 512.644 100Z" fill="#1A1A1A"/>
1076
- <path d="M413.706 481.86C451.11 481.86 481.274 451.649 481.274 414.189C481.274 376.728 451.11 346.518 413.706 346.518C376.303 346.518 346.139 376.728 346.139 414.189C346.139 451.649 376.303 481.86 413.706 481.86Z" fill="white"/>
1077
- <path d="M153.089 347.968C153.089 347.243 153.813 346.518 154.537 346.518H287.017C287.741 346.518 288.465 347.243 288.465 347.968V412.98C288.465 413.705 287.741 414.43 287.017 414.43H257.095C256.371 414.43 255.647 415.155 255.647 415.88V480.893C255.647 481.618 254.923 482.343 254.199 482.343H188.321C187.597 482.343 186.873 481.618 186.873 480.893V415.88C186.873 415.155 186.149 414.43 185.425 414.43H154.296C153.572 414.43 152.848 413.705 152.848 412.98V347.968H153.089Z" fill="white"/>
1078
- <path d="M347.587 153.17C346.863 153.17 346.139 153.895 346.139 154.621V223.017C346.139 259.269 376.303 288.513 413.706 288.513C451.11 288.513 481.274 259.269 481.274 223.017V154.621C481.274 153.895 480.55 153.17 479.826 153.17H347.345H347.587Z" fill="white"/>
1079
- <path d="M153.33 286.339C152.848 287.306 153.33 288.272 154.537 288.272H286.776C287.742 288.272 288.465 287.306 287.983 286.339L221.863 154.139C221.381 153.172 219.933 153.172 219.45 154.139L153.33 286.339Z" fill="white"/>
1080
- </g>
1081
- <defs>
1082
- <clipPath id="clip0_1042_573">
1083
- <rect width="637" height="637" fill="white"/>
1084
- </clipPath>
1085
- </defs>
1086
- </svg>`;
1087
-
1088
- const logoDark = `<svg width="637" height="637" viewBox="0 0 637 637" fill="none" xmlns="http://www.w3.org/2000/svg">
1089
- <g clip-path="url(#clip0_1042_512)">
1090
- <rect width="637" height="637"/>
1091
- <path d="M693.161 -427.743L-623.632 1532.31L-591.107 1552.06L725.686 -407.988L693.161 -427.743Z" fill="#EC3F4A"/>
1092
- <path d="M725.695 -407.962L-591.097 1552.09L-571.511 1563.98L745.281 -396.066L725.695 -407.962Z" fill="#1A1A1A"/>
1093
- <path d="M745.281 -396.071L-571.511 1563.98L-538.986 1583.73L777.807 -376.315L745.281 -396.071Z" fill="#FF8A1D"/>
1094
- <path d="M777.815 -376.302L-538.978 1583.75L-519.391 1595.64L797.402 -364.406L777.815 -376.302Z" fill="#1A1A1A"/>
1095
- <path d="M797.394 -364.425L-519.399 1595.62L-486.874 1615.38L829.919 -344.67L797.394 -364.425Z" fill="#5EC72D"/>
1096
- <path d="M829.913 -344.684L-486.879 1615.37L-467.293 1627.26L849.499 -332.787L829.913 -344.684Z" fill="#1A1A1A"/>
1097
- <path d="M849.467 -332.791L-467.326 1627.26L-434.801 1647.01L881.992 -313.035L849.467 -332.791Z" fill="#42C3F7"/>
1098
- <path d="M882 -313.038L-434.792 1647.01L-415.206 1658.91L901.587 -301.142L882 -313.038Z" fill="#1A1A1A"/>
1099
- <path d="M673.55 -439.635L-643.242 1520.42L-623.656 1532.31L693.136 -427.738L673.55 -439.635Z" fill="#1A1A1A"/>
1100
- <path d="M512.644 100H121.718C109.652 100 100 109.667 100 121.751V513.277C100 525.361 109.652 535.028 121.718 535.028H512.644C524.71 535.028 534.362 525.361 534.362 513.277V121.751C534.362 109.667 524.71 100 512.644 100Z" fill="white"/>
1101
- <path d="M413.706 481.86C451.11 481.86 481.274 451.649 481.274 414.189C481.274 376.728 451.11 346.518 413.706 346.518C376.303 346.518 346.139 376.728 346.139 414.189C346.139 451.649 376.303 481.86 413.706 481.86Z" fill="#1A1A1A"/>
1102
- <path d="M153.089 347.968C153.089 347.243 153.813 346.518 154.537 346.518H287.017C287.741 346.518 288.465 347.243 288.465 347.968V412.98C288.465 413.705 287.741 414.43 287.017 414.43H257.095C256.371 414.43 255.647 415.155 255.647 415.88V480.893C255.647 481.618 254.923 482.343 254.199 482.343H188.321C187.597 482.343 186.873 481.618 186.873 480.893V415.88C186.873 415.155 186.149 414.43 185.425 414.43H154.296C153.572 414.43 152.848 413.705 152.848 412.98V347.968H153.089Z" fill="#1A1A1A"/>
1103
- <path d="M347.587 153.17C346.863 153.17 346.139 153.895 346.139 154.621V223.017C346.139 259.269 376.303 288.513 413.706 288.513C451.11 288.513 481.274 259.269 481.274 223.017V154.621C481.274 153.895 480.55 153.17 479.826 153.17H347.345H347.587Z" fill="#1A1A1A"/>
1104
- <path d="M153.33 286.339C152.848 287.306 153.33 288.272 154.537 288.272H286.776C287.742 288.272 288.465 287.306 287.983 286.339L221.863 154.139C221.381 153.172 219.933 153.172 219.45 154.139L153.33 286.339Z" fill="#1A1A1A"/>
1105
- </g>
1106
- <defs>
1107
- <clipPath id="clip0_1042_512">
1108
- <rect width="637" height="637" fill="white"/>
1109
- </clipPath>
1110
- </defs>
1111
- </svg>`;
1112
-
1113
- // Theme management
1114
- function getSystemTheme() {
1115
- return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
1116
- }
1117
-
1118
- function applyTheme(theme) {
1119
- if (theme === 'system') {
1120
- theme = getSystemTheme();
1121
- }
1122
- document.body.setAttribute('data-theme', theme);
1123
-
1124
- // Update logo based on theme
1125
- const logoMark = document.getElementById('logoMark');
1126
- if (logoMark) {
1127
- logoMark.innerHTML = theme === 'dark' ? logoDark : logoLight;
1128
- }
1129
- }
1130
-
1131
- function getThemeIcon(theme) {
1132
- const icons = {
1133
- light: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />',
1134
- dark: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />',
1135
- system: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />'
1136
- };
1137
- return icons[theme] || icons.system;
1138
- }
1139
-
1140
- function updateThemeIcon(theme) {
1141
- const iconElement = document.getElementById('currentThemeIcon');
1142
- if (iconElement) {
1143
- iconElement.innerHTML = getThemeIcon(theme);
1144
- }
1145
- }
1146
-
1147
- function toggleThemeDropdown() {
1148
- const dropdown = document.getElementById('themeDropdown');
1149
- dropdown.classList.toggle('open');
1150
- }
1151
-
1152
- function setTheme(theme) {
1153
- localStorage.setItem('theme', theme);
1154
- applyTheme(theme);
1155
- updateThemeIcon(theme);
1156
-
1157
- // Update button states
1158
- document.querySelectorAll('.theme-option').forEach(btn => {
1159
- btn.classList.toggle('active', btn.getAttribute('data-theme') === theme);
1160
- });
1161
-
1162
- // Close dropdown
1163
- document.getElementById('themeDropdown').classList.remove('open');
1164
- }
1165
-
1166
- // Close dropdown when clicking outside
1167
- document.addEventListener('click', (e) => {
1168
- const switcher = document.querySelector('.theme-switcher');
1169
- if (!switcher.contains(e.target)) {
1170
- document.getElementById('themeDropdown').classList.remove('open');
1171
- }
1172
- });
1173
-
1174
- // Initialize theme
1175
- const savedTheme = localStorage.getItem('theme') || 'dark';
1176
- setTheme(savedTheme);
1177
- updateThemeIcon(savedTheme);
1178
-
1179
- // Listen for system theme changes
1180
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
1181
- if (localStorage.getItem('theme') === 'system') {
1182
- applyTheme('system');
1183
- }
1184
- });
1185
-
1186
- // Command selection
1187
- function selectCommand(command) {
1188
- if (!command) return;
1189
-
1190
- currentCommand = command;
1191
- localStorage.setItem('lastCommand', command);
1192
-
1193
- // Enable send button
1194
- document.getElementById('sendButton').disabled = false;
1195
-
1196
- // Clear response status
1197
- document.getElementById('statusDot').className = 'status-dot';
1198
- document.getElementById('statusText').textContent = 'Ready';
1199
- document.getElementById('responseTime').textContent = '';
1200
- }
1201
-
1202
- // Toggle command dropdown
1203
- function toggleCommandDropdown() {
1204
- const trigger = document.getElementById('commandTrigger');
1205
- const options = document.getElementById('commandOptions');
1206
-
1207
- const isOpen = trigger.classList.contains('open');
1208
-
1209
- if (isOpen) {
1210
- trigger.classList.remove('open');
1211
- options.classList.remove('open');
1212
- } else {
1213
- trigger.classList.add('open');
1214
- options.classList.add('open');
1215
- }
1216
- }
1217
-
1218
- // Select command from custom dropdown
1219
- function selectCommandFromDropdown(command) {
1220
- // Update trigger text
1221
- const trigger = document.getElementById('commandTrigger');
1222
- const triggerText = trigger.querySelector('.command-trigger-text');
1223
-
1224
- // Mark trigger as selected
1225
- trigger.classList.add('selected');
1226
- triggerText.textContent = command;
1227
-
1228
- // Update option selection states
1229
- document.querySelectorAll('.command-option').forEach(option => {
1230
- option.classList.toggle('selected', option.getAttribute('data-command') === command);
1231
- });
1232
-
1233
- // Close dropdown
1234
- trigger.classList.remove('open');
1235
- document.getElementById('commandOptions').classList.remove('open');
1236
-
1237
- // Call original select command logic
1238
- selectCommand(command);
1239
- }
1240
-
1241
- // Close dropdown when clicking outside
1242
- document.addEventListener('click', (e) => {
1243
- const dropdown = document.querySelector('.command-dropdown');
1244
- if (!dropdown.contains(e.target)) {
1245
- const trigger = document.getElementById('commandTrigger');
1246
- const options = document.getElementById('commandOptions');
1247
- trigger.classList.remove('open');
1248
- options.classList.remove('open');
1249
- }
1250
- });
1251
-
1252
- // Send request
1253
- async function sendRequest() {
1254
- if (!currentCommand) {
1255
- alert('Please select a command first');
1256
- return;
1257
- }
1258
-
1259
- responseStartTime = Date.now();
1260
-
1261
- // Update status to sending
1262
- document.getElementById('statusDot').className = 'status-dot';
1263
- document.getElementById('statusText').textContent = 'Sending...';
1264
-
1265
- try {
1266
- const requestId = document.getElementById('requestIdInput').value;
1267
- const bodyStr = document.getElementById('requestBody').value;
1268
-
1269
- const commandData = JSON.parse(bodyStr);
1270
-
1271
- const command = {
1272
- type: currentCommand,
1273
- data: commandData
1274
- };
1275
-
1276
- if (requestId) {
1277
- command.requestId = requestId;
1278
- }
1279
-
1280
- const response = await fetch(`${API_BASE}/command`, {
1281
- method: 'POST',
1282
- headers: {
1283
- 'Content-Type': 'application/json'
1284
- },
1285
- body: JSON.stringify(command)
1286
- });
1287
-
1288
- const responseTime = Date.now() - responseStartTime;
1289
- const result = await response.json();
1290
-
1291
- const statusDot = document.getElementById('statusDot');
1292
- const statusText = document.getElementById('statusText');
1293
-
1294
- if (response.ok && result.status === 'ack') {
1295
- statusDot.className = 'status-dot success';
1296
- statusText.textContent = 'ACK';
1297
- } else {
1298
- statusDot.className = 'status-dot error';
1299
- statusText.textContent = 'ERROR';
1300
- }
1301
- document.getElementById('responseTime').textContent = `${responseTime}ms`;
1302
-
1303
- // Auto refresh messages after sending
1304
- setTimeout(loadMessages, 500);
1305
-
1306
- } catch (error) {
1307
- const responseTime = Date.now() - responseStartTime;
1308
-
1309
- document.getElementById('responseTime').textContent = `${responseTime}ms`;
1310
- document.getElementById('statusDot').className = 'status-dot error';
1311
- document.getElementById('statusText').textContent = 'ERROR';
1312
- }
1313
- }
1314
-
1315
- // Load registry and populate command dropdown
1316
- async function loadRegistry() {
1317
- try {
1318
- const response = await fetch(`${API_BASE}/registry`);
1319
- const data = await response.json();
1320
-
1321
- const commandHandlers = data.commandHandlers || [];
1322
- const commandsWithMetadata = data.commandsWithMetadata || [];
1323
-
1324
- availableCommands = commandHandlers;
1325
-
1326
- // Populate command dropdown with detailed 3-line format
1327
- const commandOptions = document.getElementById('commandOptions');
1328
- commandOptions.innerHTML = '';
1329
-
1330
- commandsWithMetadata.forEach(cmd => {
1331
- const option = document.createElement('button');
1332
- option.className = 'command-option';
1333
- option.setAttribute('data-command', cmd.name);
1334
- option.onclick = () => selectCommandFromDropdown(cmd.name);
1335
-
1336
- // Create 3-line format: command name, org, package
1337
- const packageParts = (cmd.package || 'core').split('/');
1338
- const org = packageParts.length > 1 ? packageParts[0] : '@auto-engineer';
1339
- const packageName = packageParts.length > 1 ? packageParts[1] : packageParts[0];
1340
-
1341
- option.innerHTML = `
1342
- <div class="command-option-name">${cmd.name}</div>
1343
- <div class="command-option-org">${org}</div>
1344
- <div class="command-option-package">${packageName}</div>
1345
- `;
1346
- option.title = `${cmd.name}\n\nOrganization: ${org}\nPackage: ${packageName}\nType: ${cmd.type || 'command'}\n\nDescription: ${cmd.description || 'No description available'}`;
1347
-
1348
- commandOptions.appendChild(option);
1349
- });
1350
-
1351
- // Restore last selected command
1352
- const lastCommand = localStorage.getItem('lastCommand');
1353
- if (lastCommand && commandHandlers.includes(lastCommand)) {
1354
- selectCommandFromDropdown(lastCommand);
1355
- }
1356
-
1357
- } catch (error) {
1358
- console.error('Failed to load registry:', error);
1359
- }
1360
- }
1361
-
1362
- // Load messages
1363
- async function loadMessages() {
1364
- try {
1365
- const messagesResponse = await fetch(`${API_BASE}/messages?count=500`);
1366
- currentMessages = await messagesResponse.json();
1367
-
1368
- // Load available sessions for filter dropdown
1369
- const sessionsResponse = await fetch(`${API_BASE}/sessions`);
1370
- availableSessions = await sessionsResponse.json();
1371
- updateSessionFilter();
1372
-
1373
- applyFilters();
1374
- } catch (error) {
1375
- console.error('Failed to load messages:', error);
1376
- document.getElementById('messagesList').innerHTML = '<div class="empty">Failed to load messages</div>';
1377
- }
1378
- }
1379
-
1380
- // Update session filter dropdown
1381
- function updateSessionFilter() {
1382
- const sessionFilter = document.getElementById('sessionFilter');
1383
- const currentValue = sessionFilter.value;
1384
-
1385
- sessionFilter.innerHTML = '<option value="">All Sessions</option>';
1386
-
1387
- availableSessions
1388
- .sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt))
1389
- .forEach(session => {
1390
- const startTime = new Date(session.startedAt).toLocaleString();
1391
- const option = document.createElement('option');
1392
- option.value = session.sessionId;
1393
- option.textContent = `${session.sessionId.substr(0, 12)}... (${startTime})`;
1394
- sessionFilter.appendChild(option);
1395
- });
1396
-
1397
- // Restore previous selection
1398
- sessionFilter.value = currentValue;
1399
- }
1400
-
1401
- // Apply message filters
1402
- function applyFilters() {
1403
- const messageTypeFilter = document.getElementById('messageTypeFilter').value;
1404
- const sessionFilter = document.getElementById('sessionFilter').value;
1405
- const messageNameFilter = document.getElementById('messageNameFilter').value.toLowerCase();
1406
- const correlationIdFilter = document.getElementById('correlationIdFilter').value.toLowerCase();
1407
-
1408
- filteredMessages = currentMessages.filter(message => {
1409
- // Message type filter
1410
- if (messageTypeFilter && message.messageType !== messageTypeFilter) {
1411
- return false;
1412
- }
1413
-
1414
- // Session filter
1415
- if (sessionFilter && message.sessionId !== sessionFilter) {
1416
- return false;
1417
- }
1418
-
1419
- // Message name filter
1420
- if (messageNameFilter && !message.message.type.toLowerCase().includes(messageNameFilter)) {
1421
- return false;
1422
- }
1423
-
1424
- // Correlation ID filter
1425
- if (correlationIdFilter) {
1426
- const correlationId = message.message.correlationId || '';
1427
- if (!correlationId.toLowerCase().includes(correlationIdFilter)) {
1428
- return false;
1429
- }
1430
- }
1431
-
1432
- return true;
1433
- });
1434
-
1435
- displayMessages();
1436
- }
1437
-
1438
- // Display messages with preserved expansion state
1439
- function displayMessages() {
1440
- const tableBody = document.getElementById('messagesTableBody');
1441
- const countElement = document.getElementById('messageCount');
1442
- const statsElement = document.getElementById('filterStats');
1443
-
1444
- if (filteredMessages.length === 0) {
1445
- tableBody.innerHTML = '<tr><td colspan="8" style="text-align: center; padding: 32px; color: var(--text-tertiary);">No messages match the current filters</td></tr>';
1446
- countElement.textContent = '0';
1447
- statsElement.textContent = `Showing 0 of ${currentMessages.length} messages`;
1448
- return;
1449
- }
1450
-
1451
- // Update count and stats
1452
- countElement.textContent = filteredMessages.length;
1453
- const commandCount = filteredMessages.filter(m => m.messageType === 'command').length;
1454
- const eventCount = filteredMessages.filter(m => m.messageType === 'event').length;
1455
- statsElement.textContent = `Showing ${filteredMessages.length} of ${currentMessages.length} messages (${commandCount} commands, ${eventCount} events)`;
1456
-
1457
- // Display messages in chronological order (oldest first)
1458
- const messagesHtml = filteredMessages
1459
- .slice()
1460
- .sort((a, b) => parseInt(a.position) - parseInt(b.position))
1461
- .map((message) => {
1462
- const timeObj = new Date(message.timestamp);
1463
- const time = timeObj.toLocaleTimeString('en-US', {
1464
- hour12: false,
1465
- hour: '2-digit',
1466
- minute: '2-digit',
1467
- second: '2-digit'
1468
- }) + '.' + timeObj.getMilliseconds().toString().padStart(3, '0');
1469
- const isExpanded = expandedMessages.has(String(message.position));
1470
-
1471
- const correlationId = message.message.correlationId || 'none';
1472
- const requestId = message.message.requestId || 'none';
1473
- const sessionId = message.sessionId || 'none';
1474
-
1475
- let rowsHtml = `
1476
- <tr class="message-row ${isExpanded ? 'expanded' : ''}" data-position="${message.position}">
1477
- <td class="message-cell expand-cell">
1478
- <span class="expand-btn">${isExpanded ? '-' : '+'}</span>
1479
- </td>
1480
- <td class="message-cell type-cell">
1481
- <span class="message-type ${message.messageType}">${message.messageType.toUpperCase()}</span>
1482
- </td>
1483
- <td class="message-cell name-cell" title="${message.message.type}">
1484
- <span class="message-name">${message.message.type}</span>
1485
- </td>
1486
- <td class="message-cell id-cell" title="${requestId}">
1487
- ${requestId.length > 35 ? requestId.substring(0, 35) + '...' : requestId}
1488
- </td>
1489
- <td class="message-cell id-cell" title="${correlationId}">
1490
- ${correlationId.length > 35 ? correlationId.substring(0, 35) + '...' : correlationId}
1491
- </td>
1492
- <td class="message-cell id-cell" title="${sessionId}">
1493
- ${sessionId.length > 35 ? sessionId.substring(0, 35) + '...' : sessionId}
1494
- </td>
1495
- <td class="message-cell position-cell">${message.position}</td>
1496
- <td class="message-cell time-cell">${time}</td>
1497
- </tr>
1498
- `;
1499
-
1500
- if (isExpanded) {
1501
- const messageDataJson = JSON.stringify(message.message.data, null, 2);
1502
- rowsHtml += `
1503
- <tr class="message-details-row" style="background: #1a202c;">
1504
- <td colspan="8" style="padding: 16px; border-top: 1px solid #4a5568;">
1505
- <div style="display: flex; flex-direction: column; gap: 8px;">
1506
- <span style="color: #a0aec0; font-size: 12px; font-weight: 600;">MESSAGE DATA</span>
1507
- <pre style="background: #2d3748; color: #e2e8f0; padding: 16px; border-radius: 6px; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; line-height: 1.4; overflow-x: auto; border: 1px solid #4a5568; margin: 0;">${messageDataJson}</pre>
1508
- </div>
1509
- </td>
1510
- </tr>
1511
- `;
1512
- }
1513
-
1514
- return rowsHtml;
1515
- })
1516
- .join('');
1517
-
1518
- // Always update to ensure expanded content shows properly
1519
- tableBody.innerHTML = messagesHtml;
1520
- autoScrollToBottom();
1521
- }
1522
-
1523
- // Toggle message details (like CloudWatch)
1524
- function toggleMessage(position) {
1525
- console.log('toggleMessage called with position:', position, 'type:', typeof position);
1526
- console.log('Current expandedMessages:', Array.from(expandedMessages));
1527
-
1528
- // Ensure position is a string for consistency
1529
- const positionStr = String(position);
1530
-
1531
- if (expandedMessages.has(positionStr)) {
1532
- expandedMessages.delete(positionStr);
1533
- console.log('Collapsed message', positionStr);
1534
- } else {
1535
- expandedMessages.add(positionStr);
1536
- console.log('Expanded message', positionStr);
1537
- }
1538
-
1539
- console.log('New expandedMessages:', Array.from(expandedMessages));
1540
- displayMessages();
1541
- }
1542
-
1543
- // Handle click events on table for expansion
1544
- document.addEventListener('click', (e) => {
1545
- // Check if click is on expand button or expand cell (but not resize handle)
1546
- const expandBtn = e.target.closest('.expand-btn');
1547
- const expandCell = e.target.closest('.expand-cell');
1548
- const resizeHandle = e.target.closest('.resize-handle');
1549
-
1550
- if ((expandBtn || expandCell) && !resizeHandle) {
1551
- const row = e.target.closest('tr[data-position]');
1552
- if (row) {
1553
- const position = row.getAttribute('data-position');
1554
- if (position) {
1555
- e.stopPropagation();
1556
- e.preventDefault();
1557
- console.log('Toggling message position:', position);
1558
- toggleMessage(position);
1559
- }
1560
- }
1561
- }
1562
- });
1563
-
1564
- // Column resizing functionality
1565
- function initColumnResize() {
1566
- const table = document.querySelector('.messages-table');
1567
- if (!table) return;
1568
-
1569
- // Mouse down on resize handle
1570
- document.addEventListener('mousedown', (e) => {
1571
- if (!e.target.classList.contains('resize-handle')) return;
1572
-
1573
- e.preventDefault();
1574
- isResizing = true;
1575
-
1576
- const headerCell = e.target.parentElement;
1577
- const column = headerCell.dataset.column;
1578
-
1579
- currentColumn = column;
1580
- startX = e.clientX;
1581
- startWidth = parseInt(getComputedStyle(headerCell).width);
1582
-
1583
- document.body.classList.add('resizing');
1584
- table.classList.add('resizing');
1585
- });
1586
-
1587
- // Mouse move during resize
1588
- document.addEventListener('mousemove', (e) => {
1589
- if (!isResizing || !currentColumn) return;
1590
-
1591
- const deltaX = e.clientX - startX;
1592
- const newWidth = Math.max(30, startWidth + deltaX);
1593
-
1594
- columnWidths[currentColumn] = newWidth;
1595
- updateColumnWidths();
1596
- });
1597
-
1598
- // Mouse up to end resize
1599
- document.addEventListener('mouseup', () => {
1600
- if (isResizing) {
1601
- isResizing = false;
1602
- currentColumn = null;
1603
-
1604
- document.body.classList.remove('resizing');
1605
- const table = document.querySelector('.messages-table');
1606
- if (table) table.classList.remove('resizing');
1607
-
1608
- // Save column widths to localStorage
1609
- localStorage.setItem('columnWidths', JSON.stringify(columnWidths));
1610
- }
1611
- });
1612
- }
1613
-
1614
- // Update column widths in the table
1615
- function updateColumnWidths() {
1616
- const headers = document.querySelectorAll('.messages-header-cell');
1617
- headers.forEach((cell, index) => {
1618
- const column = cell.dataset.column;
1619
- if (column && columnWidths[column]) {
1620
- cell.style.width = columnWidths[column] + 'px';
1621
- cell.style.minWidth = columnWidths[column] + 'px';
1622
- cell.style.maxWidth = columnWidths[column] + 'px';
1623
- }
1624
- });
1625
-
1626
- // Apply to colgroup if it exists
1627
- const colgroup = document.querySelector('.messages-table colgroup');
1628
- if (colgroup) {
1629
- const cols = colgroup.querySelectorAll('col');
1630
- headers.forEach((cell, index) => {
1631
- const column = cell.dataset.column;
1632
- if (column && columnWidths[column] && cols[index]) {
1633
- cols[index].style.width = columnWidths[column] + 'px';
1634
- }
1635
- });
1636
- }
1637
- }
1638
-
1639
- // Load saved column widths
1640
- function loadColumnWidths() {
1641
- const saved = localStorage.getItem('columnWidths');
1642
- if (saved) {
1643
- try {
1644
- const parsed = JSON.parse(saved);
1645
- columnWidths = { ...defaultColumnWidths, ...parsed };
1646
- } catch (e) {
1647
- columnWidths = { ...defaultColumnWidths };
1648
- }
1649
- }
1650
- }
1651
-
1652
- // Copy message data to request body field
1653
- function copyMessageData(position, messageData) {
1654
- try {
1655
- const requestBodyField = document.getElementById('requestBody');
1656
- if (requestBodyField) {
1657
- // Parse and re-stringify to ensure proper formatting
1658
- const parsed = JSON.parse(messageData);
1659
- requestBodyField.value = JSON.stringify(parsed, null, 2);
1660
-
1661
- // Visual feedback
1662
- const copyBtn = window.event ? window.event.target : null;
1663
- if (copyBtn) {
1664
- const originalText = copyBtn.textContent;
1665
- copyBtn.textContent = 'Copied!';
1666
- copyBtn.style.background = 'var(--success)';
1667
- copyBtn.style.color = 'white';
1668
-
1669
- setTimeout(() => {
1670
- copyBtn.textContent = originalText;
1671
- copyBtn.style.background = '';
1672
- copyBtn.style.color = '';
1673
- }, 1500);
1674
- }
1675
- }
1676
- } catch (error) {
1677
- console.error('Failed to copy message data:', error);
1678
-
1679
- // Error feedback
1680
- const copyBtn = window.event ? window.event.target : null;
1681
- if (copyBtn) {
1682
- const originalText = copyBtn.textContent;
1683
- copyBtn.textContent = 'Error';
1684
- copyBtn.style.background = 'var(--error)';
1685
- copyBtn.style.color = 'white';
1686
-
1687
- setTimeout(() => {
1688
- copyBtn.textContent = originalText;
1689
- copyBtn.style.background = '';
1690
- copyBtn.style.color = '';
1691
- }, 1500);
1692
- }
1693
- }
1694
- }
1695
-
1696
- // Auto-scroll to bottom when new messages arrive
1697
- let lastMessageCount = 0;
1698
- function autoScrollToBottom() {
1699
- if (currentMessages.length > lastMessageCount) {
1700
- const messagesContainer = document.querySelector('.messages-list');
1701
- if (messagesContainer) {
1702
- messagesContainer.scrollTop = messagesContainer.scrollHeight;
1703
- }
1704
- lastMessageCount = currentMessages.length;
1705
- }
1706
- }
1707
-
1708
- // Clear filters
1709
- function clearFilters() {
1710
- document.getElementById('messageTypeFilter').value = '';
1711
- document.getElementById('sessionFilter').value = '';
1712
- document.getElementById('messageNameFilter').value = '';
1713
- document.getElementById('correlationIdFilter').value = '';
1714
- applyFilters();
1715
- }
1716
-
1717
- // Keyboard shortcuts
1718
- document.addEventListener('keydown', (e) => {
1719
- if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
1720
- sendRequest();
1721
- }
1722
- });
1723
-
1724
- // WebSocket connection for real-time updates
1725
- const ws = new WebSocket(`ws://${window.location.hostname}:${window.location.port}`);
1726
-
1727
- ws.onopen = () => {
1728
- console.log('WebSocket connected');
1729
- };
1730
-
1731
- ws.onmessage = (event) => {
1732
- const message = JSON.parse(event.data);
1733
- if (message.type === 'event' || message.type === 'state') {
1734
- // Reload messages when events occur
1735
- loadMessages();
1736
- } else if (message.type === 'commandError') {
1737
- // Show command error in response
1738
- document.getElementById('responsePreview').textContent = JSON.stringify({
1739
- status: 'error',
1740
- commandId: message.commandId,
1741
- error: message.error,
1742
- timestamp: message.timestamp
1743
- }, null, 2);
1744
- document.getElementById('statusDot').className = 'status-dot error';
1745
- document.getElementById('statusText').textContent = 'Command Failed';
1746
- document.getElementById('responseTime').textContent = 'Async Error';
1747
- }
1748
- };
1749
-
1750
- ws.onerror = (error) => {
1751
- console.error('WebSocket error:', error);
1752
- };
1753
-
1754
- ws.onclose = () => {
1755
- console.log('WebSocket disconnected');
1756
- };
1757
-
1758
- // Initial load
1759
- loadColumnWidths();
1760
- loadRegistry();
1761
- loadMessages();
1762
-
1763
- // Initialize column resize functionality
1764
- setTimeout(() => {
1765
- initColumnResize();
1766
- updateColumnWidths();
1767
- }, 100);
1768
-
1769
- // Auto-refresh
1770
- setInterval(loadRegistry, 5000);
1771
- setInterval(loadMessages, 2000);
1772
- </script>
1773
- </body>
1774
- </html>