agent-relay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +22 -0
  3. package/PROTOCOL.md +319 -0
  4. package/README.md +791 -0
  5. package/dist/cli/index.d.ts +7 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +1591 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/daemon/connection.d.ts +60 -0
  10. package/dist/daemon/connection.d.ts.map +1 -0
  11. package/dist/daemon/connection.js +245 -0
  12. package/dist/daemon/connection.js.map +1 -0
  13. package/dist/daemon/index.d.ts +4 -0
  14. package/dist/daemon/index.d.ts.map +1 -0
  15. package/dist/daemon/index.js +4 -0
  16. package/dist/daemon/index.js.map +1 -0
  17. package/dist/daemon/router.d.ts +72 -0
  18. package/dist/daemon/router.d.ts.map +1 -0
  19. package/dist/daemon/router.js +183 -0
  20. package/dist/daemon/router.js.map +1 -0
  21. package/dist/daemon/server.d.ts +52 -0
  22. package/dist/daemon/server.d.ts.map +1 -0
  23. package/dist/daemon/server.js +186 -0
  24. package/dist/daemon/server.js.map +1 -0
  25. package/dist/dashboard/public/index.html +690 -0
  26. package/dist/dashboard/server.d.ts +2 -0
  27. package/dist/dashboard/server.d.ts.map +1 -0
  28. package/dist/dashboard/server.js +220 -0
  29. package/dist/dashboard/server.js.map +1 -0
  30. package/dist/games/index.d.ts +2 -0
  31. package/dist/games/index.d.ts.map +1 -0
  32. package/dist/games/index.js +2 -0
  33. package/dist/games/index.js.map +1 -0
  34. package/dist/games/tictactoe.d.ts +24 -0
  35. package/dist/games/tictactoe.d.ts.map +1 -0
  36. package/dist/games/tictactoe.js +160 -0
  37. package/dist/games/tictactoe.js.map +1 -0
  38. package/dist/hooks/inbox-check/hook.d.ts +28 -0
  39. package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
  40. package/dist/hooks/inbox-check/hook.js +97 -0
  41. package/dist/hooks/inbox-check/hook.js.map +1 -0
  42. package/dist/hooks/inbox-check/index.d.ts +8 -0
  43. package/dist/hooks/inbox-check/index.d.ts.map +1 -0
  44. package/dist/hooks/inbox-check/index.js +8 -0
  45. package/dist/hooks/inbox-check/index.js.map +1 -0
  46. package/dist/hooks/inbox-check/types.d.ts +31 -0
  47. package/dist/hooks/inbox-check/types.d.ts.map +1 -0
  48. package/dist/hooks/inbox-check/types.js +5 -0
  49. package/dist/hooks/inbox-check/types.js.map +1 -0
  50. package/dist/hooks/inbox-check/utils.d.ts +44 -0
  51. package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
  52. package/dist/hooks/inbox-check/utils.js +107 -0
  53. package/dist/hooks/inbox-check/utils.js.map +1 -0
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/protocol/framing.d.ts +32 -0
  59. package/dist/protocol/framing.d.ts.map +1 -0
  60. package/dist/protocol/framing.js +71 -0
  61. package/dist/protocol/framing.js.map +1 -0
  62. package/dist/protocol/index.d.ts +3 -0
  63. package/dist/protocol/index.d.ts.map +1 -0
  64. package/dist/protocol/index.js +3 -0
  65. package/dist/protocol/index.js.map +1 -0
  66. package/dist/protocol/types.d.ts +104 -0
  67. package/dist/protocol/types.d.ts.map +1 -0
  68. package/dist/protocol/types.js +6 -0
  69. package/dist/protocol/types.js.map +1 -0
  70. package/dist/state/agent-state.d.ts +40 -0
  71. package/dist/state/agent-state.d.ts.map +1 -0
  72. package/dist/state/agent-state.js +120 -0
  73. package/dist/state/agent-state.js.map +1 -0
  74. package/dist/storage/adapter.d.ts +29 -0
  75. package/dist/storage/adapter.d.ts.map +1 -0
  76. package/dist/storage/adapter.js +2 -0
  77. package/dist/storage/adapter.js.map +1 -0
  78. package/dist/storage/sqlite-adapter.d.ts +15 -0
  79. package/dist/storage/sqlite-adapter.d.ts.map +1 -0
  80. package/dist/storage/sqlite-adapter.js +116 -0
  81. package/dist/storage/sqlite-adapter.js.map +1 -0
  82. package/dist/supervisor/inbox.d.ts +38 -0
  83. package/dist/supervisor/inbox.d.ts.map +1 -0
  84. package/dist/supervisor/inbox.js +162 -0
  85. package/dist/supervisor/inbox.js.map +1 -0
  86. package/dist/supervisor/index.d.ts +10 -0
  87. package/dist/supervisor/index.d.ts.map +1 -0
  88. package/dist/supervisor/index.js +10 -0
  89. package/dist/supervisor/index.js.map +1 -0
  90. package/dist/supervisor/spawner.d.ts +54 -0
  91. package/dist/supervisor/spawner.d.ts.map +1 -0
  92. package/dist/supervisor/spawner.js +282 -0
  93. package/dist/supervisor/spawner.js.map +1 -0
  94. package/dist/supervisor/state.d.ts +132 -0
  95. package/dist/supervisor/state.d.ts.map +1 -0
  96. package/dist/supervisor/state.js +465 -0
  97. package/dist/supervisor/state.js.map +1 -0
  98. package/dist/supervisor/supervisor.d.ts +67 -0
  99. package/dist/supervisor/supervisor.d.ts.map +1 -0
  100. package/dist/supervisor/supervisor.js +263 -0
  101. package/dist/supervisor/supervisor.js.map +1 -0
  102. package/dist/supervisor/types.d.ts +139 -0
  103. package/dist/supervisor/types.d.ts.map +1 -0
  104. package/dist/supervisor/types.js +12 -0
  105. package/dist/supervisor/types.js.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +2 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/dist/utils/name-generator.d.ts +17 -0
  111. package/dist/utils/name-generator.d.ts.map +1 -0
  112. package/dist/utils/name-generator.js +52 -0
  113. package/dist/utils/name-generator.js.map +1 -0
  114. package/dist/webhook/spawner.d.ts +79 -0
  115. package/dist/webhook/spawner.d.ts.map +1 -0
  116. package/dist/webhook/spawner.js +288 -0
  117. package/dist/webhook/spawner.js.map +1 -0
  118. package/dist/wrapper/client.d.ts +72 -0
  119. package/dist/wrapper/client.d.ts.map +1 -0
  120. package/dist/wrapper/client.js +306 -0
  121. package/dist/wrapper/client.js.map +1 -0
  122. package/dist/wrapper/inbox.d.ts +37 -0
  123. package/dist/wrapper/inbox.d.ts.map +1 -0
  124. package/dist/wrapper/inbox.js +73 -0
  125. package/dist/wrapper/inbox.js.map +1 -0
  126. package/dist/wrapper/index.d.ts +4 -0
  127. package/dist/wrapper/index.d.ts.map +1 -0
  128. package/dist/wrapper/index.js +7 -0
  129. package/dist/wrapper/index.js.map +1 -0
  130. package/dist/wrapper/parser.d.ts +94 -0
  131. package/dist/wrapper/parser.d.ts.map +1 -0
  132. package/dist/wrapper/parser.js +360 -0
  133. package/dist/wrapper/parser.js.map +1 -0
  134. package/dist/wrapper/pty-wrapper.d.ts +125 -0
  135. package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
  136. package/dist/wrapper/pty-wrapper.js +494 -0
  137. package/dist/wrapper/pty-wrapper.js.map +1 -0
  138. package/dist/wrapper/tmux-wrapper.d.ts +131 -0
  139. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
  140. package/dist/wrapper/tmux-wrapper.js +427 -0
  141. package/dist/wrapper/tmux-wrapper.js.map +1 -0
  142. package/install.sh +69 -0
  143. package/package.json +82 -0
@@ -0,0 +1,690 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Agent Relay</title>
5
+ <style>
6
+ :root {
7
+ --bg-primary: #0a0a0f;
8
+ --bg-secondary: #12121a;
9
+ --bg-card: rgba(25, 20, 15, 0.8);
10
+ --border-color: rgba(251, 146, 60, 0.2);
11
+ --border-glow: rgba(251, 146, 60, 0.5);
12
+ --primary: #f97316;
13
+ --primary-light: #fb923c;
14
+ --accent: #eab308;
15
+ --accent-light: #facc15;
16
+ --text: #e2e8f0;
17
+ --text-muted: #94a3b8;
18
+ --success: #10b981;
19
+ --error: #ef4444;
20
+ --gradient-1: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
21
+ --gradient-2: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
22
+ --gradient-3: linear-gradient(135deg, #fb923c 0%, #fbbf24 100%);
23
+ --gradient-hero: linear-gradient(135deg, #f97316 0%, #ea580c 50%, #eab308 100%);
24
+ }
25
+
26
+ * { box-sizing: border-box; margin: 0; padding: 0; }
27
+
28
+ body {
29
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
30
+ background: var(--bg-primary);
31
+ color: var(--text);
32
+ min-height: 100vh;
33
+ overflow-x: hidden;
34
+ }
35
+
36
+ /* Animated background */
37
+ body::before {
38
+ content: '';
39
+ position: fixed;
40
+ top: 0; left: 0; right: 0; bottom: 0;
41
+ background:
42
+ radial-gradient(ellipse at 20% 20%, rgba(249, 115, 22, 0.15) 0%, transparent 50%),
43
+ radial-gradient(ellipse at 80% 80%, rgba(234, 179, 8, 0.15) 0%, transparent 50%),
44
+ radial-gradient(ellipse at 40% 60%, rgba(251, 191, 36, 0.1) 0%, transparent 40%);
45
+ animation: bgPulse 8s ease-in-out infinite;
46
+ pointer-events: none;
47
+ z-index: 0;
48
+ }
49
+
50
+ @keyframes bgPulse {
51
+ 0%, 100% { opacity: 1; transform: scale(1); }
52
+ 50% { opacity: 0.8; transform: scale(1.05); }
53
+ }
54
+
55
+ .container {
56
+ position: relative;
57
+ z-index: 1;
58
+ max-width: 1600px;
59
+ margin: 0 auto;
60
+ display: grid;
61
+ grid-template-columns: 380px 1fr;
62
+ gap: 24px;
63
+ height: 100vh;
64
+ padding: 20px;
65
+ }
66
+
67
+ .sidebar {
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: 16px;
71
+ overflow-y: auto;
72
+ padding-right: 8px;
73
+ }
74
+
75
+ .main-content {
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 20px;
79
+ overflow: hidden;
80
+ }
81
+
82
+ /* Glowing header */
83
+ .header {
84
+ display: flex;
85
+ justify-content: space-between;
86
+ align-items: center;
87
+ padding: 20px 24px;
88
+ background: var(--bg-card);
89
+ border-radius: 16px;
90
+ border: 1px solid var(--border-color);
91
+ backdrop-filter: blur(20px);
92
+ position: relative;
93
+ overflow: hidden;
94
+ }
95
+
96
+ .header::before {
97
+ content: '';
98
+ position: absolute;
99
+ top: 0; left: 0; right: 0;
100
+ height: 2px;
101
+ background: var(--gradient-hero);
102
+ animation: shimmer 3s linear infinite;
103
+ }
104
+
105
+ @keyframes shimmer {
106
+ 0% { background-position: -200% 0; }
107
+ 100% { background-position: 200% 0; }
108
+ }
109
+
110
+ .logo {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 12px;
114
+ }
115
+
116
+ .logo-icon {
117
+ width: 44px;
118
+ height: 44px;
119
+ background: var(--gradient-1);
120
+ border-radius: 12px;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ font-size: 20px;
125
+ animation: logoFloat 3s ease-in-out infinite;
126
+ box-shadow: 0 4px 20px rgba(249, 115, 22, 0.4);
127
+ }
128
+
129
+ @keyframes logoFloat {
130
+ 0%, 100% { transform: translateY(0); }
131
+ 50% { transform: translateY(-3px); }
132
+ }
133
+
134
+ h1 {
135
+ font-size: 1.5em;
136
+ font-weight: 700;
137
+ background: var(--gradient-hero);
138
+ background-size: 200% auto;
139
+ -webkit-background-clip: text;
140
+ -webkit-text-fill-color: transparent;
141
+ background-clip: text;
142
+ animation: gradientText 4s linear infinite;
143
+ }
144
+
145
+ @keyframes gradientText {
146
+ 0% { background-position: 0% center; }
147
+ 100% { background-position: 200% center; }
148
+ }
149
+
150
+ h2 {
151
+ color: var(--text);
152
+ font-weight: 600;
153
+ }
154
+
155
+ /* Agent Cards */
156
+ .agent-card {
157
+ background: var(--bg-card);
158
+ padding: 20px;
159
+ border-radius: 16px;
160
+ border: 1px solid var(--border-color);
161
+ backdrop-filter: blur(20px);
162
+ position: relative;
163
+ overflow: hidden;
164
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
165
+ animation: cardFadeIn 0.5s ease-out backwards;
166
+ }
167
+
168
+ .agent-card::before {
169
+ content: '';
170
+ position: absolute;
171
+ top: 0; left: 0;
172
+ width: 4px;
173
+ height: 100%;
174
+ background: var(--gradient-1);
175
+ opacity: 0.5;
176
+ transition: all 0.3s;
177
+ }
178
+
179
+ .agent-card:hover {
180
+ transform: translateX(8px) scale(1.02);
181
+ border-color: var(--border-glow);
182
+ box-shadow:
183
+ 0 0 30px rgba(249, 115, 22, 0.2),
184
+ 0 10px 40px rgba(0, 0, 0, 0.3);
185
+ }
186
+
187
+ .agent-card:hover::before {
188
+ opacity: 1;
189
+ width: 6px;
190
+ }
191
+
192
+ .agent-card.active {
193
+ border-color: var(--primary);
194
+ background: rgba(249, 115, 22, 0.1);
195
+ }
196
+
197
+ .agent-card.active::before {
198
+ opacity: 1;
199
+ animation: borderPulse 2s ease-in-out infinite;
200
+ }
201
+
202
+ @keyframes borderPulse {
203
+ 0%, 100% { opacity: 1; }
204
+ 50% { opacity: 0.5; }
205
+ }
206
+
207
+ @keyframes cardFadeIn {
208
+ from { opacity: 0; transform: translateX(-20px); }
209
+ to { opacity: 1; transform: translateX(0); }
210
+ }
211
+
212
+ .agent-card:nth-child(1) { animation-delay: 0.1s; }
213
+ .agent-card:nth-child(2) { animation-delay: 0.2s; }
214
+ .agent-card:nth-child(3) { animation-delay: 0.3s; }
215
+ .agent-card:nth-child(4) { animation-delay: 0.4s; }
216
+
217
+ .agent-header {
218
+ display: flex;
219
+ justify-content: space-between;
220
+ align-items: center;
221
+ margin-bottom: 12px;
222
+ }
223
+
224
+ .agent-name {
225
+ font-weight: 700;
226
+ color: #fff;
227
+ font-size: 1.15em;
228
+ letter-spacing: -0.02em;
229
+ }
230
+
231
+ .agent-role {
232
+ color: var(--text-muted);
233
+ font-size: 0.85em;
234
+ margin-bottom: 16px;
235
+ display: flex;
236
+ align-items: center;
237
+ gap: 8px;
238
+ }
239
+
240
+ .agent-role .cli-badge {
241
+ background: rgba(6, 182, 212, 0.15);
242
+ color: var(--accent);
243
+ padding: 2px 8px;
244
+ border-radius: 6px;
245
+ font-size: 0.8em;
246
+ font-weight: 500;
247
+ }
248
+
249
+ .agent-status {
250
+ background: rgba(0, 0, 0, 0.3);
251
+ padding: 12px 16px;
252
+ border-radius: 10px;
253
+ font-size: 0.9em;
254
+ color: var(--accent-light);
255
+ border: 1px solid rgba(6, 182, 212, 0.2);
256
+ display: flex;
257
+ align-items: center;
258
+ gap: 8px;
259
+ }
260
+
261
+ .status-dot {
262
+ width: 8px;
263
+ height: 8px;
264
+ border-radius: 50%;
265
+ background: var(--accent);
266
+ animation: statusPulse 2s ease-in-out infinite;
267
+ }
268
+
269
+ @keyframes statusPulse {
270
+ 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(6, 182, 212, 0.4); }
271
+ 50% { opacity: 0.6; box-shadow: 0 0 0 6px rgba(6, 182, 212, 0); }
272
+ }
273
+
274
+ .badge {
275
+ background: var(--gradient-2);
276
+ color: #000;
277
+ padding: 4px 12px;
278
+ border-radius: 20px;
279
+ font-size: 0.75em;
280
+ font-weight: 700;
281
+ letter-spacing: 0.5px;
282
+ animation: badgePulse 2s ease-in-out infinite;
283
+ box-shadow: 0 2px 10px rgba(251, 191, 36, 0.3);
284
+ }
285
+
286
+ @keyframes badgePulse {
287
+ 0%, 100% { transform: scale(1); }
288
+ 50% { transform: scale(1.05); }
289
+ }
290
+
291
+ /* Activity Log */
292
+ .activity-log {
293
+ background: var(--bg-card);
294
+ border-radius: 20px;
295
+ border: 1px solid var(--border-color);
296
+ backdrop-filter: blur(20px);
297
+ display: flex;
298
+ flex-direction: column;
299
+ flex: 1;
300
+ overflow: hidden;
301
+ position: relative;
302
+ }
303
+
304
+ .activity-log::before {
305
+ content: '';
306
+ position: absolute;
307
+ top: 0; left: 0; right: 0;
308
+ height: 1px;
309
+ background: linear-gradient(90deg, transparent, var(--primary), var(--accent), transparent);
310
+ }
311
+
312
+ .log-header {
313
+ padding: 20px 24px;
314
+ border-bottom: 1px solid var(--border-color);
315
+ display: flex;
316
+ justify-content: space-between;
317
+ align-items: center;
318
+ background: rgba(0, 0, 0, 0.2);
319
+ }
320
+
321
+ .log-header h2 {
322
+ display: flex;
323
+ align-items: center;
324
+ gap: 10px;
325
+ }
326
+
327
+ .live-indicator {
328
+ display: flex;
329
+ align-items: center;
330
+ gap: 6px;
331
+ font-size: 0.8em;
332
+ color: var(--text-muted);
333
+ background: rgba(0, 0, 0, 0.3);
334
+ padding: 6px 12px;
335
+ border-radius: 20px;
336
+ }
337
+
338
+ .live-dot {
339
+ width: 8px;
340
+ height: 8px;
341
+ border-radius: 50%;
342
+ background: var(--success);
343
+ animation: livePulse 1.5s ease-in-out infinite;
344
+ }
345
+
346
+ @keyframes livePulse {
347
+ 0%, 100% { opacity: 1; transform: scale(1); }
348
+ 50% { opacity: 0.5; transform: scale(0.8); }
349
+ }
350
+
351
+ .log-content {
352
+ overflow-y: auto;
353
+ padding: 0;
354
+ flex: 1;
355
+ display: flex;
356
+ flex-direction: column-reverse;
357
+ }
358
+
359
+ .message {
360
+ padding: 20px 24px;
361
+ border-bottom: 1px solid rgba(249, 115, 22, 0.1);
362
+ display: flex;
363
+ gap: 16px;
364
+ transition: background 0.3s;
365
+ }
366
+
367
+ .message:hover {
368
+ background: rgba(249, 115, 22, 0.05);
369
+ }
370
+
371
+ @keyframes messageFadeIn {
372
+ from { opacity: 0; transform: translateY(20px); }
373
+ to { opacity: 1; transform: translateY(0); }
374
+ }
375
+
376
+ .message:last-child { border-bottom: none; }
377
+
378
+ .msg-avatar {
379
+ width: 48px;
380
+ height: 48px;
381
+ border-radius: 14px;
382
+ background: var(--gradient-1);
383
+ color: #fff;
384
+ display: flex;
385
+ align-items: center;
386
+ justify-content: center;
387
+ font-weight: 700;
388
+ font-size: 1.1em;
389
+ flex-shrink: 0;
390
+ box-shadow: 0 4px 15px rgba(249, 115, 22, 0.3);
391
+ transition: transform 0.3s, box-shadow 0.3s;
392
+ }
393
+
394
+ .message:hover .msg-avatar {
395
+ transform: scale(1.1) rotate(5deg);
396
+ box-shadow: 0 6px 25px rgba(249, 115, 22, 0.5);
397
+ }
398
+
399
+ .msg-body { flex: 1; min-width: 0; }
400
+
401
+ .msg-meta {
402
+ display: flex;
403
+ align-items: center;
404
+ gap: 10px;
405
+ margin-bottom: 10px;
406
+ flex-wrap: wrap;
407
+ }
408
+
409
+ .msg-sender {
410
+ color: #fff;
411
+ font-weight: 700;
412
+ font-size: 1.05em;
413
+ }
414
+
415
+ .msg-arrow {
416
+ color: var(--primary);
417
+ font-size: 1.2em;
418
+ animation: arrowBounce 1s ease-in-out infinite;
419
+ }
420
+
421
+ @keyframes arrowBounce {
422
+ 0%, 100% { transform: translateX(0); }
423
+ 50% { transform: translateX(3px); }
424
+ }
425
+
426
+ .msg-target {
427
+ color: var(--accent);
428
+ font-weight: 600;
429
+ }
430
+
431
+ .msg-time {
432
+ color: var(--text-muted);
433
+ font-size: 0.8em;
434
+ margin-left: auto;
435
+ background: rgba(0, 0, 0, 0.2);
436
+ padding: 4px 10px;
437
+ border-radius: 12px;
438
+ }
439
+
440
+ .msg-text {
441
+ font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
442
+ font-size: 0.9em;
443
+ white-space: pre-wrap;
444
+ color: var(--text);
445
+ background: rgba(0, 0, 0, 0.3);
446
+ padding: 16px;
447
+ border-radius: 12px;
448
+ border: 1px solid var(--border-color);
449
+ line-height: 1.6;
450
+ }
451
+
452
+ /* Connection Status */
453
+ #connection-status {
454
+ font-size: 0.85em;
455
+ font-weight: 600;
456
+ padding: 8px 16px;
457
+ border-radius: 24px;
458
+ background: rgba(16, 185, 129, 0.15);
459
+ color: var(--success);
460
+ border: 1px solid rgba(16, 185, 129, 0.3);
461
+ display: flex;
462
+ align-items: center;
463
+ gap: 8px;
464
+ transition: all 0.3s;
465
+ }
466
+
467
+ #connection-status .dot {
468
+ width: 8px;
469
+ height: 8px;
470
+ border-radius: 50%;
471
+ background: var(--success);
472
+ animation: connPulse 2s ease-in-out infinite;
473
+ }
474
+
475
+ @keyframes connPulse {
476
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
477
+ 50% { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
478
+ }
479
+
480
+ .disconnected {
481
+ background: rgba(239, 68, 68, 0.15) !important;
482
+ color: var(--error) !important;
483
+ border-color: rgba(239, 68, 68, 0.3) !important;
484
+ }
485
+
486
+ .disconnected .dot {
487
+ background: var(--error) !important;
488
+ animation: none !important;
489
+ }
490
+
491
+ .empty-state {
492
+ text-align: center;
493
+ padding: 60px 40px;
494
+ color: var(--text-muted);
495
+ }
496
+
497
+ .empty-state-icon {
498
+ font-size: 48px;
499
+ margin-bottom: 16px;
500
+ opacity: 0.5;
501
+ }
502
+
503
+ .empty-state-text {
504
+ font-size: 1.1em;
505
+ line-height: 1.6;
506
+ }
507
+
508
+ /* Scrollbar */
509
+ ::-webkit-scrollbar { width: 8px; }
510
+ ::-webkit-scrollbar-track { background: transparent; }
511
+ ::-webkit-scrollbar-thumb {
512
+ background: linear-gradient(180deg, var(--primary), var(--accent));
513
+ border-radius: 4px;
514
+ }
515
+ ::-webkit-scrollbar-thumb:hover {
516
+ background: linear-gradient(180deg, var(--primary-light), var(--accent-light));
517
+ }
518
+
519
+ .last-active {
520
+ margin-top: 12px;
521
+ font-size: 0.75em;
522
+ color: var(--text-muted);
523
+ text-align: right;
524
+ opacity: 0.7;
525
+ }
526
+ </style>
527
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
528
+ </head>
529
+ <body>
530
+ <div class="container">
531
+ <div class="sidebar">
532
+ <div class="header">
533
+ <div class="logo">
534
+ <div class="logo-icon">⚡</div>
535
+ <h1>Agent Relay</h1>
536
+ </div>
537
+ <div id="connection-status">
538
+ <span class="dot"></span>
539
+ <span>Live</span>
540
+ </div>
541
+ </div>
542
+ <div id="agents">
543
+ <!-- Agents injected here -->
544
+ </div>
545
+ </div>
546
+
547
+ <div class="main-content">
548
+ <div class="activity-log">
549
+ <div class="log-header">
550
+ <h2>
551
+ <span>Live Activity</span>
552
+ </h2>
553
+ <div class="live-indicator">
554
+ <span class="live-dot"></span>
555
+ <span>Real-time • Port 3888</span>
556
+ </div>
557
+ </div>
558
+ <div class="log-content" id="log">
559
+ <!-- Messages injected here -->
560
+ </div>
561
+ </div>
562
+ </div>
563
+ </div>
564
+
565
+ <script>
566
+ const agentsContainer = document.getElementById('agents');
567
+ const logContainer = document.getElementById('log');
568
+ const statusDiv = document.getElementById('connection-status');
569
+
570
+ // Track last data hash to prevent unnecessary re-renders
571
+ let lastDataHash = '';
572
+
573
+ function connect() {
574
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
575
+ const ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
576
+
577
+ ws.onopen = () => {
578
+ statusDiv.innerHTML = '<span class="dot"></span><span>Live</span>';
579
+ statusDiv.classList.remove('disconnected');
580
+ };
581
+
582
+ ws.onclose = () => {
583
+ statusDiv.innerHTML = '<span class="dot"></span><span>Offline</span>';
584
+ statusDiv.classList.add('disconnected');
585
+ setTimeout(connect, 3000);
586
+ };
587
+
588
+ ws.onmessage = (e) => {
589
+ // Skip re-render if data hasn't changed
590
+ const dataHash = e.data;
591
+ if (dataHash === lastDataHash) return;
592
+ lastDataHash = dataHash;
593
+
594
+ const data = JSON.parse(e.data);
595
+ render(data);
596
+ };
597
+ }
598
+
599
+ function render(data) {
600
+ // Render Agents
601
+ if (data.agents && data.agents.length > 0) {
602
+ const newAgentsHTML = data.agents.map((a, i) => `
603
+ <div class="agent-card ${a.messageCount > 0 ? 'active' : ''}">
604
+ <div class="agent-header">
605
+ <div class="agent-name">${a.name}</div>
606
+ ${a.messageCount > 0 ? `<span class="badge">${a.messageCount} NEW</span>` : ''}
607
+ </div>
608
+ <div class="agent-role">
609
+ ${a.role}
610
+ <span class="cli-badge">${a.cli}</span>
611
+ </div>
612
+ <div class="agent-status">
613
+ <span class="status-dot"></span>
614
+ ${a.status && a.status !== 'Idle' ? a.status : 'Ready'}
615
+ </div>
616
+ <div class="last-active">
617
+ ${a.lastActive ? '⏱ ' + timeAgo(new Date(a.lastActive)) : '🌙 No recent activity'}
618
+ </div>
619
+ </div>
620
+ `).join('');
621
+
622
+ if (agentsContainer.innerHTML !== newAgentsHTML) {
623
+ agentsContainer.innerHTML = newAgentsHTML;
624
+ }
625
+ } else if (!agentsContainer.querySelector('.empty-state')) {
626
+ agentsContainer.innerHTML = `
627
+ <div class="empty-state">
628
+ <div class="empty-state-icon">🤖</div>
629
+ <div class="empty-state-text">Waiting for agents to connect...</div>
630
+ </div>
631
+ `;
632
+ }
633
+
634
+ // Render Messages (Activity Log)
635
+ if (data.messages && data.messages.length > 0) {
636
+ const currentCount = logContainer.querySelectorAll('.message').length;
637
+ const newCount = data.messages.length;
638
+
639
+ if (currentCount !== newCount) {
640
+ logContainer.innerHTML = data.messages.map(m => createMessageHTML(m)).join('');
641
+ }
642
+ } else if (!logContainer.querySelector('.empty-state')) {
643
+ logContainer.innerHTML = `
644
+ <div class="empty-state">
645
+ <div class="empty-state-icon">💬</div>
646
+ <div class="empty-state-text">No messages yet.<br>Start your agents to see the conversation!</div>
647
+ </div>
648
+ `;
649
+ }
650
+ }
651
+
652
+ function createMessageHTML(m) {
653
+ const initials = m.from.substring(0, 2).toUpperCase();
654
+ return `
655
+ <div class="message">
656
+ <div class="msg-avatar">${initials}</div>
657
+ <div class="msg-body">
658
+ <div class="msg-meta">
659
+ <span class="msg-sender">${m.from}</span>
660
+ <span class="msg-arrow">→</span>
661
+ <span class="msg-target">${m.to}</span>
662
+ <span class="msg-time">${new Date(m.timestamp).toLocaleTimeString()}</span>
663
+ </div>
664
+ <div class="msg-text">${escapeHtml(m.content)}</div>
665
+ </div>
666
+ </div>
667
+ `;
668
+ }
669
+
670
+ function escapeHtml(text) {
671
+ if (!text) return '';
672
+ const div = document.createElement('div');
673
+ div.textContent = text;
674
+ return div.innerHTML;
675
+ }
676
+
677
+ function timeAgo(date) {
678
+ const seconds = Math.floor((new Date() - date) / 1000);
679
+ if (seconds < 60) return 'just now';
680
+ const minutes = Math.floor(seconds / 60);
681
+ if (minutes < 60) return `${minutes}m ago`;
682
+ const hours = Math.floor(minutes / 60);
683
+ if (hours < 24) return `${hours}h ago`;
684
+ return `${Math.floor(hours / 24)}d ago`;
685
+ }
686
+
687
+ connect();
688
+ </script>
689
+ </body>
690
+ </html>
@@ -0,0 +1,2 @@
1
+ export declare function startDashboard(port: number, dataDir: string, dbPath?: string): Promise<void>;
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AA6BA,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgOlG"}