@teneo-protocol/sdk 1.0.1 → 2.0.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.
- package/.github/workflows/publish-npm.yml +8 -6
- package/CHANGELOG.md +265 -0
- package/README.md +406 -53
- package/dist/core/websocket-client.d.ts +12 -0
- package/dist/core/websocket-client.d.ts.map +1 -1
- package/dist/core/websocket-client.js +22 -2
- package/dist/core/websocket-client.js.map +1 -1
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
- package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.d.ts +5 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.js +23 -1
- package/dist/handlers/message-handlers/index.js.map +1 -1
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/types.d.ts +2 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -1
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
- package/dist/managers/agent-room-manager.d.ts +222 -0
- package/dist/managers/agent-room-manager.d.ts.map +1 -0
- package/dist/managers/agent-room-manager.js +508 -0
- package/dist/managers/agent-room-manager.js.map +1 -0
- package/dist/managers/index.d.ts +2 -0
- package/dist/managers/index.d.ts.map +1 -1
- package/dist/managers/index.js +5 -1
- package/dist/managers/index.js.map +1 -1
- package/dist/managers/room-management-manager.d.ts +213 -0
- package/dist/managers/room-management-manager.d.ts.map +1 -0
- package/dist/managers/room-management-manager.js +440 -0
- package/dist/managers/room-management-manager.js.map +1 -0
- package/dist/managers/room-manager.d.ts +4 -4
- package/dist/managers/room-manager.d.ts.map +1 -1
- package/dist/managers/room-manager.js.map +1 -1
- package/dist/teneo-sdk.d.ts +333 -13
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +468 -1
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts +63 -54
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +8 -4
- package/dist/types/config.js.map +1 -1
- package/dist/types/error-codes.d.ts +2 -0
- package/dist/types/error-codes.d.ts.map +1 -1
- package/dist/types/error-codes.js +3 -0
- package/dist/types/error-codes.js.map +1 -1
- package/dist/types/events.d.ts +132 -68
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/events.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +27 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/messages.d.ts +11396 -2559
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/messages.js +294 -27
- package/dist/types/messages.js.map +1 -1
- package/examples/.env.example +1 -1
- package/examples/agent-room-management-example.ts +334 -0
- package/examples/claude-agent-x-follower/.env.example +2 -2
- package/examples/claude-agent-x-follower/QUICKSTART.md +1 -1
- package/examples/claude-agent-x-follower/README.md +1 -1
- package/examples/n8n-teneo/.env.example +2 -2
- package/examples/n8n-teneo/README.md +1 -1
- package/examples/openai-teneo/.env.example +2 -2
- package/examples/openai-teneo/README.md +1 -1
- package/examples/production-dashboard/.env.example +2 -2
- package/examples/production-dashboard/README.md +89 -12
- package/examples/production-dashboard/public/dashboard.html +1173 -601
- package/examples/production-dashboard/server.ts +347 -5
- package/examples/room-management-example.ts +285 -0
- package/examples/usage/.env.example +1 -1
- package/examples/usage/01-connect.ts +1 -1
- package/examples/usage/02-list-agents.ts +1 -1
- package/examples/usage/03-pick-agent.ts +1 -1
- package/examples/usage/04-find-by-capability.ts +1 -1
- package/examples/usage/05-webhook-example.ts +1 -1
- package/examples/usage/06-simple-api-server.ts +1 -1
- package/examples/usage/07-event-listener.ts +1 -1
- package/examples/usage/README.md +1 -1
- package/package.json +9 -1
- package/src/core/websocket-client.ts +26 -2
- package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
- package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
- package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
- package/src/handlers/message-handlers/index.ts +19 -0
- package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
- package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
- package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
- package/src/handlers/message-handlers/types.ts +6 -0
- package/src/managers/agent-room-manager.ts +609 -0
- package/src/managers/index.ts +2 -0
- package/src/managers/room-management-manager.ts +523 -0
- package/src/managers/room-manager.ts +4 -5
- package/src/teneo-sdk.ts +505 -4
- package/src/types/config.ts +10 -5
- package/src/types/error-codes.ts +4 -0
- package/src/types/events.ts +24 -0
- package/src/types/index.ts +55 -0
- package/src/types/messages.ts +374 -41
- package/tests/integration/room-management.test.ts +514 -0
- package/tests/integration/websocket.test.ts +1 -1
- package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
- package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
- package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
- package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
- package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
- package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
- package/tests/unit/managers/agent-room-manager.test.ts +534 -0
- package/tests/unit/managers/room-management-manager.test.ts +438 -0
|
@@ -1,373 +1,607 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8"
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Teneo SDK - Production Dashboard</title>
|
|
7
7
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
8
|
<style>
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
.status-dot {
|
|
10
|
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
11
|
+
}
|
|
12
|
+
@keyframes pulse {
|
|
13
|
+
0%,
|
|
14
|
+
100% {
|
|
15
|
+
opacity: 1;
|
|
11
16
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
50% { opacity: .5; }
|
|
17
|
+
50% {
|
|
18
|
+
opacity: 0.5;
|
|
15
19
|
}
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
}
|
|
21
|
+
.event-item {
|
|
22
|
+
animation: slideIn 0.3s ease-out;
|
|
23
|
+
}
|
|
24
|
+
@keyframes slideIn {
|
|
25
|
+
from {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(-10px);
|
|
18
28
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
transform: translateY(-10px);
|
|
23
|
-
}
|
|
24
|
-
to {
|
|
25
|
-
opacity: 1;
|
|
26
|
-
transform: translateY(0);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
.scrollbar-thin::-webkit-scrollbar {
|
|
30
|
-
width: 6px;
|
|
31
|
-
}
|
|
32
|
-
.scrollbar-thin::-webkit-scrollbar-track {
|
|
33
|
-
background: #1f2937;
|
|
34
|
-
}
|
|
35
|
-
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
36
|
-
background: #4b5563;
|
|
37
|
-
border-radius: 3px;
|
|
29
|
+
to {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
transform: translateY(0);
|
|
38
32
|
}
|
|
33
|
+
}
|
|
34
|
+
.scrollbar-thin::-webkit-scrollbar {
|
|
35
|
+
width: 6px;
|
|
36
|
+
}
|
|
37
|
+
.scrollbar-thin::-webkit-scrollbar-track {
|
|
38
|
+
background: #1f2937;
|
|
39
|
+
}
|
|
40
|
+
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
41
|
+
background: #4b5563;
|
|
42
|
+
border-radius: 3px;
|
|
43
|
+
}
|
|
39
44
|
</style>
|
|
40
|
-
</head>
|
|
41
|
-
<body class="bg-gray-900 text-gray-100">
|
|
45
|
+
</head>
|
|
46
|
+
<body class="bg-gray-900 text-gray-100">
|
|
42
47
|
<div class="container mx-auto px-4 py-6 max-w-7xl">
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
<!-- Header -->
|
|
49
|
+
<div class="mb-8">
|
|
50
|
+
<h1
|
|
51
|
+
class="text-4xl font-bold mb-2 bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent"
|
|
52
|
+
>
|
|
53
|
+
Teneo SDK Production Dashboard
|
|
54
|
+
</h1>
|
|
55
|
+
<p class="text-gray-400">Comprehensive example showcasing all SDK features</p>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- Status Grid -->
|
|
59
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
60
|
+
<!-- Connection Status -->
|
|
61
|
+
<div class="bg-gray-800 rounded-lg p-4 border border-gray-700">
|
|
62
|
+
<div class="flex items-center justify-between mb-2">
|
|
63
|
+
<span class="text-gray-400 text-sm">Connection</span>
|
|
64
|
+
<div id="connection-dot" class="status-dot w-3 h-3 rounded-full bg-gray-500"></div>
|
|
65
|
+
</div>
|
|
66
|
+
<div id="connection-status" class="text-2xl font-bold text-gray-500">Connecting...</div>
|
|
49
67
|
</div>
|
|
50
68
|
|
|
51
|
-
<!-- Status
|
|
52
|
-
<div class="
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<div id="connection-status" class="text-2xl font-bold text-gray-500">Connecting...</div>
|
|
60
|
-
</div>
|
|
69
|
+
<!-- Auth Status -->
|
|
70
|
+
<div class="bg-gray-800 rounded-lg p-4 border border-gray-700">
|
|
71
|
+
<div class="flex items-center justify-between mb-2">
|
|
72
|
+
<span class="text-gray-400 text-sm">Authentication</span>
|
|
73
|
+
<div id="auth-dot" class="status-dot w-3 h-3 rounded-full bg-gray-500"></div>
|
|
74
|
+
</div>
|
|
75
|
+
<div id="auth-status" class="text-2xl font-bold text-gray-500">Pending</div>
|
|
76
|
+
</div>
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
<!-- Agents Count -->
|
|
79
|
+
<div class="bg-gray-800 rounded-lg p-4 border border-gray-700">
|
|
80
|
+
<div class="flex items-center justify-between mb-2">
|
|
81
|
+
<span class="text-gray-400 text-sm">Agents</span>
|
|
82
|
+
<svg
|
|
83
|
+
class="w-5 h-5 text-blue-400"
|
|
84
|
+
fill="none"
|
|
85
|
+
stroke="currentColor"
|
|
86
|
+
viewBox="0 0 24 24"
|
|
87
|
+
>
|
|
88
|
+
<path
|
|
89
|
+
stroke-linecap="round"
|
|
90
|
+
stroke-linejoin="round"
|
|
91
|
+
stroke-width="2"
|
|
92
|
+
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
93
|
+
></path>
|
|
94
|
+
</svg>
|
|
95
|
+
</div>
|
|
96
|
+
<div id="agents-count" class="text-2xl font-bold">0</div>
|
|
97
|
+
</div>
|
|
70
98
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
<!-- Messages Count -->
|
|
100
|
+
<div class="bg-gray-800 rounded-lg p-4 border border-gray-700">
|
|
101
|
+
<div class="flex items-center justify-between mb-2">
|
|
102
|
+
<span class="text-gray-400 text-sm">Messages</span>
|
|
103
|
+
<svg
|
|
104
|
+
class="w-5 h-5 text-green-400"
|
|
105
|
+
fill="none"
|
|
106
|
+
stroke="currentColor"
|
|
107
|
+
viewBox="0 0 24 24"
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
stroke-linecap="round"
|
|
111
|
+
stroke-linejoin="round"
|
|
112
|
+
stroke-width="2"
|
|
113
|
+
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
|
114
|
+
></path>
|
|
115
|
+
</svg>
|
|
116
|
+
</div>
|
|
117
|
+
<div id="messages-count" class="text-2xl font-bold">0</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Main Content Grid -->
|
|
122
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
123
|
+
<!-- Send Message Panel -->
|
|
124
|
+
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
125
|
+
<h2 class="text-xl font-bold mb-4 flex items-center">
|
|
126
|
+
<svg
|
|
127
|
+
class="w-6 h-6 mr-2 text-blue-400"
|
|
128
|
+
fill="none"
|
|
129
|
+
stroke="currentColor"
|
|
130
|
+
viewBox="0 0 24 24"
|
|
131
|
+
>
|
|
132
|
+
<path
|
|
133
|
+
stroke-linecap="round"
|
|
134
|
+
stroke-linejoin="round"
|
|
135
|
+
stroke-width="2"
|
|
136
|
+
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
|
|
137
|
+
></path>
|
|
138
|
+
</svg>
|
|
139
|
+
Send Message
|
|
140
|
+
</h2>
|
|
141
|
+
<div class="mb-3">
|
|
142
|
+
<label for="room-select" class="block text-sm text-gray-400 mb-2">Select Room:</label>
|
|
143
|
+
<select
|
|
144
|
+
id="room-select"
|
|
145
|
+
class="w-full bg-gray-700 rounded-lg p-2 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none"
|
|
146
|
+
>
|
|
147
|
+
<option value="">Loading rooms...</option>
|
|
148
|
+
</select>
|
|
149
|
+
</div>
|
|
150
|
+
<textarea
|
|
151
|
+
id="message-input"
|
|
152
|
+
class="w-full bg-gray-700 rounded-lg p-3 mb-3 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none resize-none"
|
|
153
|
+
rows="3"
|
|
154
|
+
placeholder="Enter your message to agents..."
|
|
155
|
+
></textarea>
|
|
156
|
+
<div class="flex gap-3">
|
|
157
|
+
<button
|
|
158
|
+
id="send-message-btn"
|
|
159
|
+
class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed"
|
|
160
|
+
disabled
|
|
161
|
+
>
|
|
162
|
+
Send Message
|
|
163
|
+
</button>
|
|
164
|
+
<button
|
|
165
|
+
id="send-wait-btn"
|
|
166
|
+
class="flex-1 bg-purple-600 hover:bg-purple-700 text-white font-semibold py-2 px-4 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed"
|
|
167
|
+
disabled
|
|
168
|
+
>
|
|
169
|
+
Send & Wait
|
|
170
|
+
</button>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
81
173
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
174
|
+
<!-- Health Status -->
|
|
175
|
+
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
|
176
|
+
<h2 class="text-xl font-bold mb-4 flex items-center">
|
|
177
|
+
<svg
|
|
178
|
+
class="w-6 h-6 mr-2 text-green-400"
|
|
179
|
+
fill="none"
|
|
180
|
+
stroke="currentColor"
|
|
181
|
+
viewBox="0 0 24 24"
|
|
182
|
+
>
|
|
183
|
+
<path
|
|
184
|
+
stroke-linecap="round"
|
|
185
|
+
stroke-linejoin="round"
|
|
186
|
+
stroke-width="2"
|
|
187
|
+
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
188
|
+
></path>
|
|
189
|
+
</svg>
|
|
190
|
+
System Health
|
|
191
|
+
</h2>
|
|
192
|
+
<div id="health-status" class="space-y-2">
|
|
193
|
+
<div class="flex justify-between items-center">
|
|
194
|
+
<span class="text-gray-400">Overall Status</span>
|
|
195
|
+
<span id="health-overall" class="text-gray-500">Loading...</span>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="flex justify-between items-center">
|
|
198
|
+
<span class="text-gray-400">Webhook Circuit</span>
|
|
199
|
+
<span id="health-circuit" class="text-gray-500">-</span>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="flex justify-between items-center">
|
|
202
|
+
<span class="text-gray-400">Rate Limiter</span>
|
|
203
|
+
<span id="health-ratelimit" class="text-gray-500">-</span>
|
|
91
204
|
</div>
|
|
205
|
+
<div class="flex justify-between items-center">
|
|
206
|
+
<span class="text-gray-400">Uptime</span>
|
|
207
|
+
<span id="health-uptime" class="text-gray-500">-</span>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Tabs -->
|
|
214
|
+
<div class="bg-gray-800 rounded-lg border border-gray-700 overflow-hidden">
|
|
215
|
+
<div class="flex border-b border-gray-700 overflow-x-auto">
|
|
216
|
+
<button
|
|
217
|
+
class="tab-btn active px-6 py-3 font-semibold whitespace-nowrap"
|
|
218
|
+
data-tab="agents"
|
|
219
|
+
>
|
|
220
|
+
Agents
|
|
221
|
+
</button>
|
|
222
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="rooms">
|
|
223
|
+
Rooms
|
|
224
|
+
</button>
|
|
225
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="room-mgmt">
|
|
226
|
+
🆕 Room Mgmt
|
|
227
|
+
</button>
|
|
228
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="agent-room">
|
|
229
|
+
🆕 Agent-Room
|
|
230
|
+
</button>
|
|
231
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="messages">
|
|
232
|
+
Messages
|
|
233
|
+
</button>
|
|
234
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="webhooks">
|
|
235
|
+
Webhooks
|
|
236
|
+
</button>
|
|
237
|
+
<button class="tab-btn px-6 py-3 font-semibold whitespace-nowrap" data-tab="events">
|
|
238
|
+
Events
|
|
239
|
+
</button>
|
|
92
240
|
</div>
|
|
93
241
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<div class="
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
242
|
+
<div class="p-6">
|
|
243
|
+
<!-- Agents Tab -->
|
|
244
|
+
<div id="agents-tab" class="tab-content">
|
|
245
|
+
<div id="agents-list" class="space-y-3 max-h-96 overflow-y-auto scrollbar-thin">
|
|
246
|
+
<p class="text-gray-500">Loading agents...</p>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- Rooms Tab -->
|
|
251
|
+
<div id="rooms-tab" class="tab-content hidden">
|
|
252
|
+
<div class="mb-4">
|
|
253
|
+
<input
|
|
254
|
+
id="room-id-input"
|
|
255
|
+
type="text"
|
|
256
|
+
class="bg-gray-700 rounded-lg p-2 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none"
|
|
257
|
+
placeholder="Room ID"
|
|
258
|
+
/>
|
|
259
|
+
<button
|
|
260
|
+
id="subscribe-room-btn"
|
|
261
|
+
class="ml-2 bg-green-600 hover:bg-green-700 text-white font-semibold py-2 px-4 rounded-lg transition"
|
|
262
|
+
>
|
|
263
|
+
Subscribe
|
|
264
|
+
</button>
|
|
265
|
+
</div>
|
|
266
|
+
<div id="rooms-list" class="space-y-3 max-h-80 overflow-y-auto scrollbar-thin">
|
|
267
|
+
<p class="text-gray-500">Loading rooms...</p>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<!-- Room Management Tab (v2.0) -->
|
|
272
|
+
<div id="room-mgmt-tab" class="tab-content hidden">
|
|
273
|
+
<!-- Room Limit Info -->
|
|
274
|
+
<div class="bg-gray-700 rounded-lg p-4 mb-4 border border-gray-600">
|
|
275
|
+
<div class="flex justify-between items-center">
|
|
276
|
+
<span class="text-gray-400">Room Capacity:</span>
|
|
277
|
+
<span id="room-limit-text" class="font-semibold">Loading...</span>
|
|
278
|
+
</div>
|
|
127
279
|
</div>
|
|
128
280
|
|
|
129
|
-
<!--
|
|
130
|
-
<div class="bg-gray-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<span id="health-uptime" class="text-gray-500">-</span>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
281
|
+
<!-- Create Room Form -->
|
|
282
|
+
<div class="bg-gray-700 rounded-lg p-4 mb-4 border border-gray-600">
|
|
283
|
+
<h3 class="font-semibold mb-3">Create New Room</h3>
|
|
284
|
+
<div class="space-y-2">
|
|
285
|
+
<input
|
|
286
|
+
id="new-room-name"
|
|
287
|
+
type="text"
|
|
288
|
+
placeholder="Room Name"
|
|
289
|
+
class="w-full bg-gray-800 rounded p-2 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none"
|
|
290
|
+
/>
|
|
291
|
+
<input
|
|
292
|
+
id="new-room-desc"
|
|
293
|
+
type="text"
|
|
294
|
+
placeholder="Description (optional)"
|
|
295
|
+
class="w-full bg-gray-800 rounded p-2 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none"
|
|
296
|
+
/>
|
|
297
|
+
<button
|
|
298
|
+
id="create-room-btn"
|
|
299
|
+
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg transition"
|
|
300
|
+
>
|
|
301
|
+
Create Room
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
155
304
|
</div>
|
|
156
|
-
</div>
|
|
157
305
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<
|
|
306
|
+
<!-- Private Rooms -->
|
|
307
|
+
<div>
|
|
308
|
+
<h3 class="font-semibold mb-2">📁 Private Rooms</h3>
|
|
309
|
+
<div
|
|
310
|
+
id="private-rooms-list"
|
|
311
|
+
class="space-y-2 max-h-96 overflow-y-auto scrollbar-thin"
|
|
312
|
+
>
|
|
313
|
+
<p class="text-gray-500">Loading...</p>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<!-- Agent-Room Management Tab (v2.0) -->
|
|
319
|
+
<div id="agent-room-tab" class="tab-content hidden">
|
|
320
|
+
<!-- Room Selector -->
|
|
321
|
+
<div class="bg-gray-700 rounded-lg p-4 mb-4 border border-gray-600">
|
|
322
|
+
<label class="block text-sm text-gray-400 mb-2">Select Room:</label>
|
|
323
|
+
<select
|
|
324
|
+
id="agent-room-select"
|
|
325
|
+
class="w-full bg-gray-800 rounded-lg p-2 text-gray-100 border border-gray-600 focus:border-blue-500 focus:outline-none"
|
|
326
|
+
>
|
|
327
|
+
<option value="">Select a room...</option>
|
|
328
|
+
</select>
|
|
166
329
|
</div>
|
|
167
330
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
331
|
+
<!-- Agents in Room -->
|
|
332
|
+
<div class="mb-4">
|
|
333
|
+
<div class="flex justify-between items-center mb-2">
|
|
334
|
+
<h3 class="font-semibold">🤖 Agents in Room</h3>
|
|
335
|
+
<span id="room-agents-count" class="text-sm text-gray-400">-</span>
|
|
336
|
+
</div>
|
|
337
|
+
<div id="room-agents-list" class="space-y-2 max-h-60 overflow-y-auto scrollbar-thin">
|
|
338
|
+
<p class="text-gray-500">Select a room first</p>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
175
341
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
</div>
|
|
342
|
+
<!-- Available Agents -->
|
|
343
|
+
<div>
|
|
344
|
+
<div class="flex justify-between items-center mb-2">
|
|
345
|
+
<h3 class="font-semibold">➕ Available to Add</h3>
|
|
346
|
+
<span id="available-agents-count" class="text-sm text-gray-400">-</span>
|
|
347
|
+
</div>
|
|
348
|
+
<div
|
|
349
|
+
id="available-agents-list"
|
|
350
|
+
class="space-y-2 max-h-60 overflow-y-auto scrollbar-thin"
|
|
351
|
+
>
|
|
352
|
+
<p class="text-gray-500">Select a room first</p>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
191
356
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
357
|
+
<!-- Messages Tab -->
|
|
358
|
+
<div id="messages-tab" class="tab-content hidden">
|
|
359
|
+
<div id="messages-list" class="space-y-4 max-h-96 overflow-y-auto scrollbar-thin">
|
|
360
|
+
<p class="text-gray-500">No messages yet. Send a message to see it here!</p>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
198
363
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
364
|
+
<!-- Webhooks Tab -->
|
|
365
|
+
<div id="webhooks-tab" class="tab-content hidden">
|
|
366
|
+
<div id="webhooks-list" class="space-y-3 max-h-96 overflow-y-auto scrollbar-thin">
|
|
367
|
+
<p class="text-gray-500">No webhooks received yet.</p>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
205
370
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
371
|
+
<!-- Events Tab -->
|
|
372
|
+
<div id="events-tab" class="tab-content hidden">
|
|
373
|
+
<div id="events-list" class="space-y-2 max-h-96 overflow-y-auto scrollbar-thin">
|
|
374
|
+
<p class="text-gray-500">Waiting for events...</p>
|
|
212
375
|
</div>
|
|
376
|
+
</div>
|
|
213
377
|
</div>
|
|
378
|
+
</div>
|
|
214
379
|
</div>
|
|
215
380
|
|
|
216
381
|
<script>
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const data = JSON.parse(e.data);
|
|
252
|
-
handleSSEEvent(data);
|
|
253
|
-
} catch (error) {
|
|
254
|
-
console.error('SSE parse error:', error);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
eventSource.addEventListener('error', () => {
|
|
259
|
-
console.error('SSE connection error');
|
|
260
|
-
setTimeout(setupEventSource, 5000);
|
|
261
|
-
});
|
|
262
|
-
}
|
|
382
|
+
// State
|
|
383
|
+
let eventSource = null;
|
|
384
|
+
let connectionStatus = "disconnected";
|
|
385
|
+
let authStatus = "pending";
|
|
386
|
+
|
|
387
|
+
// Initialize
|
|
388
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
389
|
+
setupTabs();
|
|
390
|
+
setupEventSource();
|
|
391
|
+
loadInitialData();
|
|
392
|
+
setupMessageSending();
|
|
393
|
+
setupRoomManagement();
|
|
394
|
+
setupRoomManagementV2();
|
|
395
|
+
setupAgentRoomManagement();
|
|
396
|
+
startHealthCheck();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Tab management
|
|
400
|
+
function setupTabs() {
|
|
401
|
+
document.querySelectorAll(".tab-btn").forEach((btn) => {
|
|
402
|
+
btn.addEventListener("click", () => {
|
|
403
|
+
const tab = btn.dataset.tab;
|
|
404
|
+
document
|
|
405
|
+
.querySelectorAll(".tab-btn")
|
|
406
|
+
.forEach((b) => b.classList.remove("active", "bg-gray-700", "text-blue-400"));
|
|
407
|
+
document.querySelectorAll(".tab-content").forEach((c) => c.classList.add("hidden"));
|
|
408
|
+
btn.classList.add("active", "bg-gray-700", "text-blue-400");
|
|
409
|
+
document.getElementById(`${tab}-tab`).classList.remove("hidden");
|
|
410
|
+
|
|
411
|
+
// Load data when switching to room management tab
|
|
412
|
+
if (tab === "room-mgmt") {
|
|
413
|
+
loadRoomLimitV2();
|
|
414
|
+
loadPrivateRoomsV2();
|
|
415
|
+
}
|
|
263
416
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
break;
|
|
276
|
-
case 'agent:selected':
|
|
277
|
-
addEventItem(`Agent Selected: ${data.data.agentName}`, 'blue');
|
|
278
|
-
break;
|
|
279
|
-
case 'agent:response':
|
|
280
|
-
addEventItem(`Agent Response from ${data.response.agentName}`, 'green');
|
|
281
|
-
loadMessages();
|
|
282
|
-
break;
|
|
283
|
-
case 'message:updated':
|
|
284
|
-
addEventItem(`Message Updated: Full response received`, 'green');
|
|
285
|
-
loadMessages();
|
|
286
|
-
break;
|
|
287
|
-
case 'agent:list':
|
|
288
|
-
loadAgents();
|
|
289
|
-
break;
|
|
290
|
-
case 'room:subscribed':
|
|
291
|
-
addEventItem(`Subscribed to room: ${data.data.roomId}`, 'green');
|
|
292
|
-
loadRooms();
|
|
293
|
-
loadAvailableRooms();
|
|
294
|
-
break;
|
|
295
|
-
case 'room:unsubscribed':
|
|
296
|
-
addEventItem(`Unsubscribed from room: ${data.data.roomId}`, 'yellow');
|
|
297
|
-
loadRooms();
|
|
298
|
-
loadAvailableRooms();
|
|
299
|
-
break;
|
|
300
|
-
case 'room:list':
|
|
301
|
-
addEventItem(`Room list updated (${data.rooms.length} rooms)`, 'blue');
|
|
302
|
-
break;
|
|
303
|
-
case 'webhook:received':
|
|
304
|
-
addEventItem(`Webhook: ${data.payload.event}`, 'purple');
|
|
305
|
-
loadWebhooks();
|
|
306
|
-
break;
|
|
307
|
-
case 'error':
|
|
308
|
-
addEventItem(`Error: ${data.error.message}`, 'red');
|
|
309
|
-
break;
|
|
417
|
+
// Load data when switching to agent-room tab
|
|
418
|
+
if (tab === "agent-room") {
|
|
419
|
+
loadPrivateRoomsV2().then(() => {
|
|
420
|
+
// Populate room selector after rooms are loaded
|
|
421
|
+
fetch("/api/v2/rooms/owned")
|
|
422
|
+
.then((r) => r.json())
|
|
423
|
+
.then((rooms) => {
|
|
424
|
+
populateAgentRoomSelector(rooms);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
310
427
|
}
|
|
311
|
-
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Server-Sent Events
|
|
433
|
+
function setupEventSource() {
|
|
434
|
+
eventSource = new EventSource("/api/sse");
|
|
435
|
+
|
|
436
|
+
eventSource.addEventListener("message", (e) => {
|
|
437
|
+
try {
|
|
438
|
+
const data = JSON.parse(e.data);
|
|
439
|
+
handleSSEEvent(data);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error("SSE parse error:", error);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
312
444
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
445
|
+
eventSource.addEventListener("error", () => {
|
|
446
|
+
console.error("SSE connection error");
|
|
447
|
+
setTimeout(setupEventSource, 5000);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function handleSSEEvent(data) {
|
|
452
|
+
switch (data.type) {
|
|
453
|
+
case "connection":
|
|
454
|
+
updateConnectionStatus(data.status);
|
|
455
|
+
break;
|
|
456
|
+
case "auth":
|
|
457
|
+
updateAuthStatus(data.status);
|
|
458
|
+
if (data.status === "success") {
|
|
459
|
+
// Reload available rooms after authentication
|
|
460
|
+
setTimeout(loadAvailableRooms, 500);
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
463
|
+
case "agent:selected":
|
|
464
|
+
addEventItem(`Agent Selected: ${data.data.agentName}`, "blue");
|
|
465
|
+
break;
|
|
466
|
+
case "agent:response":
|
|
467
|
+
addEventItem(`Agent Response from ${data.response.agentName}`, "green");
|
|
468
|
+
loadMessages();
|
|
469
|
+
break;
|
|
470
|
+
case "message:updated":
|
|
471
|
+
addEventItem(`Message Updated: Full response received`, "green");
|
|
472
|
+
loadMessages();
|
|
473
|
+
break;
|
|
474
|
+
case "agent:list":
|
|
475
|
+
loadAgents();
|
|
476
|
+
break;
|
|
477
|
+
case "room:subscribed":
|
|
478
|
+
addEventItem(`Subscribed to room: ${data.data.roomId}`, "green");
|
|
479
|
+
loadRooms();
|
|
480
|
+
loadAvailableRooms();
|
|
481
|
+
break;
|
|
482
|
+
case "room:unsubscribed":
|
|
483
|
+
addEventItem(`Unsubscribed from room: ${data.data.roomId}`, "yellow");
|
|
484
|
+
loadRooms();
|
|
485
|
+
loadAvailableRooms();
|
|
486
|
+
break;
|
|
487
|
+
case "room:list":
|
|
488
|
+
addEventItem(`Room list updated (${data.rooms.length} rooms)`, "blue");
|
|
489
|
+
break;
|
|
490
|
+
case "room:created":
|
|
491
|
+
addEventItem(`Room created: ${data.room.name}`, "green");
|
|
492
|
+
loadRoomLimitV2();
|
|
493
|
+
loadPrivateRoomsV2();
|
|
494
|
+
loadAvailableRooms();
|
|
495
|
+
break;
|
|
496
|
+
case "room:updated":
|
|
497
|
+
addEventItem(`Room updated: ${data.room.name}`, "blue");
|
|
498
|
+
loadPrivateRoomsV2();
|
|
499
|
+
loadAvailableRooms();
|
|
500
|
+
break;
|
|
501
|
+
case "room:deleted":
|
|
502
|
+
addEventItem(`Room deleted: ${data.roomId}`, "yellow");
|
|
503
|
+
loadRoomLimitV2();
|
|
504
|
+
loadPrivateRoomsV2();
|
|
505
|
+
loadAvailableRooms();
|
|
506
|
+
break;
|
|
507
|
+
case "agent_room:agent_added":
|
|
508
|
+
addEventItem(`Agent added to room: ${data.agentId}`, "green");
|
|
509
|
+
// Reload agent lists if we're viewing that room
|
|
510
|
+
const currentRoom = document.getElementById("agent-room-select").value;
|
|
511
|
+
if (currentRoom === data.roomId) {
|
|
512
|
+
loadRoomAgentsV2(data.roomId);
|
|
513
|
+
loadAvailableAgentsV2(data.roomId);
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
case "agent_room:agent_removed":
|
|
517
|
+
addEventItem(`Agent removed from room: ${data.agentId}`, "yellow");
|
|
518
|
+
// Reload agent lists if we're viewing that room
|
|
519
|
+
const currentRoom2 = document.getElementById("agent-room-select").value;
|
|
520
|
+
if (currentRoom2 === data.roomId) {
|
|
521
|
+
loadRoomAgentsV2(data.roomId);
|
|
522
|
+
loadAvailableAgentsV2(data.roomId);
|
|
523
|
+
}
|
|
524
|
+
break;
|
|
525
|
+
case "webhook:received":
|
|
526
|
+
addEventItem(`Webhook: ${data.payload.event}`, "purple");
|
|
527
|
+
loadWebhooks();
|
|
528
|
+
break;
|
|
529
|
+
case "error":
|
|
530
|
+
addEventItem(`Error: ${data.error.message}`, "red");
|
|
531
|
+
break;
|
|
324
532
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Load initial data
|
|
536
|
+
async function loadInitialData() {
|
|
537
|
+
await Promise.all([
|
|
538
|
+
loadAgents(),
|
|
539
|
+
loadRooms(),
|
|
540
|
+
loadAvailableRooms(),
|
|
541
|
+
loadMessages(),
|
|
542
|
+
loadWebhooks(),
|
|
543
|
+
loadEvents(),
|
|
544
|
+
loadMetrics(),
|
|
545
|
+
loadRoomLimitV2(),
|
|
546
|
+
loadPrivateRoomsV2()
|
|
547
|
+
]);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Load agents
|
|
551
|
+
async function loadAgents() {
|
|
552
|
+
try {
|
|
553
|
+
const response = await fetch("/api/agents");
|
|
554
|
+
const agents = await response.json();
|
|
555
|
+
document.getElementById("agents-count").textContent = agents.length;
|
|
556
|
+
|
|
557
|
+
const list = document.getElementById("agents-list");
|
|
558
|
+
if (agents.length === 0) {
|
|
559
|
+
list.innerHTML = '<p class="text-gray-500">No agents available</p>';
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
list.innerHTML = agents
|
|
564
|
+
.map(
|
|
565
|
+
(agent) => `
|
|
340
566
|
<div class="bg-gray-700 rounded-lg p-4 border border-gray-600">
|
|
341
567
|
<div class="flex items-center justify-between mb-2">
|
|
342
568
|
<span class="font-semibold">${agent.name}</span>
|
|
343
|
-
<span class="text-xs px-2 py-1 rounded ${agent.status ===
|
|
569
|
+
<span class="text-xs px-2 py-1 rounded ${agent.status === "online" ? "bg-green-600" : "bg-gray-600"}">${agent.status || "unknown"}</span>
|
|
344
570
|
</div>
|
|
345
|
-
<p class="text-sm text-gray-400">${agent.description ||
|
|
346
|
-
${
|
|
571
|
+
<p class="text-sm text-gray-400">${agent.description || "No description"}</p>
|
|
572
|
+
${
|
|
573
|
+
agent.capabilities
|
|
574
|
+
? `
|
|
347
575
|
<div class="mt-2 flex flex-wrap gap-1">
|
|
348
|
-
${agent.capabilities.map(cap => `<span class="text-xs px-2 py-1 bg-blue-900 rounded">${cap.name}</span>`).join(
|
|
576
|
+
${agent.capabilities.map((cap) => `<span class="text-xs px-2 py-1 bg-blue-900 rounded">${cap.name}</span>`).join("")}
|
|
349
577
|
</div>
|
|
350
|
-
`
|
|
578
|
+
`
|
|
579
|
+
: ""
|
|
580
|
+
}
|
|
351
581
|
</div>
|
|
352
|
-
`
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
582
|
+
`
|
|
583
|
+
)
|
|
584
|
+
.join("");
|
|
585
|
+
} catch (error) {
|
|
586
|
+
console.error("Failed to load agents:", error);
|
|
356
587
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Load rooms
|
|
591
|
+
async function loadRooms() {
|
|
592
|
+
try {
|
|
593
|
+
const response = await fetch("/api/rooms");
|
|
594
|
+
const rooms = await response.json();
|
|
595
|
+
|
|
596
|
+
const list = document.getElementById("rooms-list");
|
|
597
|
+
if (rooms.length === 0) {
|
|
598
|
+
list.innerHTML = '<p class="text-gray-500">No rooms available</p>';
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
list.innerHTML = rooms
|
|
603
|
+
.map(
|
|
604
|
+
(room) => `
|
|
371
605
|
<div class="bg-gray-700 rounded-lg p-4 border border-gray-600">
|
|
372
606
|
<div class="flex items-center justify-between">
|
|
373
607
|
<div>
|
|
@@ -379,98 +613,114 @@
|
|
|
379
613
|
</button>
|
|
380
614
|
</div>
|
|
381
615
|
</div>
|
|
382
|
-
`
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
616
|
+
`
|
|
617
|
+
)
|
|
618
|
+
.join("");
|
|
619
|
+
} catch (error) {
|
|
620
|
+
console.error("Failed to load rooms:", error);
|
|
386
621
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Load available rooms for sending messages
|
|
625
|
+
async function loadAvailableRooms() {
|
|
626
|
+
try {
|
|
627
|
+
const response = await fetch("/api/rooms/available");
|
|
628
|
+
const rooms = await response.json();
|
|
629
|
+
|
|
630
|
+
const select = document.getElementById("room-select");
|
|
631
|
+
const sendBtn = document.getElementById("send-message-btn");
|
|
632
|
+
const sendWaitBtn = document.getElementById("send-wait-btn");
|
|
633
|
+
|
|
634
|
+
if (rooms.length === 0) {
|
|
635
|
+
select.innerHTML =
|
|
636
|
+
'<option value="">No rooms available - Subscribe to a room first</option>';
|
|
637
|
+
sendBtn.disabled = true;
|
|
638
|
+
sendWaitBtn.disabled = true;
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Build options with room type indicators
|
|
643
|
+
const options = rooms
|
|
644
|
+
.map((room) => {
|
|
645
|
+
const typeLabel =
|
|
646
|
+
room.type === "private" ? "🔒" : room.type === "subscribed" ? "📌" : "🌐";
|
|
647
|
+
const displayName = room.name || room.id;
|
|
648
|
+
return `<option value="${room.id}">${typeLabel} ${displayName}</option>`;
|
|
649
|
+
})
|
|
650
|
+
.join("");
|
|
651
|
+
|
|
652
|
+
select.innerHTML = options;
|
|
653
|
+
|
|
654
|
+
// Enable buttons only if connected
|
|
655
|
+
if (connectionStatus === "connected") {
|
|
656
|
+
sendBtn.disabled = false;
|
|
657
|
+
sendWaitBtn.disabled = false;
|
|
658
|
+
}
|
|
659
|
+
} catch (error) {
|
|
660
|
+
console.error("Failed to load available rooms:", error);
|
|
661
|
+
const select = document.getElementById("room-select");
|
|
662
|
+
select.innerHTML = '<option value="">Error loading rooms</option>';
|
|
424
663
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Load messages
|
|
667
|
+
async function loadMessages() {
|
|
668
|
+
try {
|
|
669
|
+
const response = await fetch("/api/messages");
|
|
670
|
+
const messages = await response.json();
|
|
671
|
+
document.getElementById("messages-count").textContent = messages.length;
|
|
672
|
+
|
|
673
|
+
const list = document.getElementById("messages-list");
|
|
674
|
+
if (messages.length === 0) {
|
|
675
|
+
list.innerHTML = '<p class="text-gray-500">No messages yet</p>';
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
list.innerHTML = messages
|
|
680
|
+
.map(
|
|
681
|
+
(msg) => `
|
|
440
682
|
<div class="bg-gray-700 rounded-lg p-4 border border-gray-600">
|
|
441
683
|
<div class="flex items-center justify-between mb-2">
|
|
442
684
|
<span class="text-sm text-gray-400">${new Date(msg.timestamp).toLocaleTimeString()}</span>
|
|
443
|
-
<span class="text-xs px-2 py-1 rounded ${msg.response ?
|
|
444
|
-
${msg.response ?
|
|
685
|
+
<span class="text-xs px-2 py-1 rounded ${msg.response ? "bg-green-600" : "bg-yellow-600"}">
|
|
686
|
+
${msg.response ? "Responded" : "Pending"}
|
|
445
687
|
</span>
|
|
446
688
|
</div>
|
|
447
689
|
<p class="text-sm mb-2">${msg.content}</p>
|
|
448
|
-
${
|
|
690
|
+
${
|
|
691
|
+
msg.response
|
|
692
|
+
? `
|
|
449
693
|
<div class="mt-3 p-3 bg-gray-800 rounded border-l-4 border-green-500">
|
|
450
694
|
<p class="text-xs text-gray-400 mb-1">Response from ${msg.response.agentName}:</p>
|
|
451
695
|
<p class="text-sm">${msg.response.humanized || msg.response.content}</p>
|
|
452
696
|
</div>
|
|
453
|
-
`
|
|
697
|
+
`
|
|
698
|
+
: ""
|
|
699
|
+
}
|
|
454
700
|
</div>
|
|
455
|
-
`
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
701
|
+
`
|
|
702
|
+
)
|
|
703
|
+
.join("");
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error("Failed to load messages:", error);
|
|
459
706
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Load webhooks
|
|
710
|
+
async function loadWebhooks() {
|
|
711
|
+
try {
|
|
712
|
+
const response = await fetch("/api/webhooks");
|
|
713
|
+
const webhooks = await response.json();
|
|
714
|
+
|
|
715
|
+
const list = document.getElementById("webhooks-list");
|
|
716
|
+
if (webhooks.length === 0) {
|
|
717
|
+
list.innerHTML = '<p class="text-gray-500">No webhooks received yet</p>';
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
list.innerHTML = webhooks
|
|
722
|
+
.map(
|
|
723
|
+
(wh) => `
|
|
474
724
|
<div class="bg-gray-700 rounded-lg p-3 border border-gray-600">
|
|
475
725
|
<div class="flex items-center justify-between mb-1">
|
|
476
726
|
<span class="font-mono text-sm text-purple-400">${wh.event}</span>
|
|
@@ -478,25 +728,29 @@
|
|
|
478
728
|
</div>
|
|
479
729
|
<pre class="text-xs text-gray-400 overflow-x-auto">${JSON.stringify(wh.data, null, 2)}</pre>
|
|
480
730
|
</div>
|
|
481
|
-
`
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
731
|
+
`
|
|
732
|
+
)
|
|
733
|
+
.join("");
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.error("Failed to load webhooks:", error);
|
|
485
736
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// Load events
|
|
740
|
+
async function loadEvents() {
|
|
741
|
+
try {
|
|
742
|
+
const response = await fetch("/api/events");
|
|
743
|
+
const events = await response.json();
|
|
744
|
+
|
|
745
|
+
const list = document.getElementById("events-list");
|
|
746
|
+
if (events.length === 0) {
|
|
747
|
+
list.innerHTML = '<p class="text-gray-500">No events yet</p>';
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
list.innerHTML = events
|
|
752
|
+
.map(
|
|
753
|
+
(evt) => `
|
|
500
754
|
<div class="event-item bg-gray-700 rounded p-2 border border-gray-600">
|
|
501
755
|
<div class="flex items-center justify-between">
|
|
502
756
|
<span class="font-mono text-xs text-blue-400">${evt.type}</span>
|
|
@@ -504,213 +758,531 @@
|
|
|
504
758
|
</div>
|
|
505
759
|
<p class="text-xs text-gray-400 mt-1">${JSON.stringify(evt.data)}</p>
|
|
506
760
|
</div>
|
|
507
|
-
`
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
761
|
+
`
|
|
762
|
+
)
|
|
763
|
+
.join("");
|
|
764
|
+
} catch (error) {
|
|
765
|
+
console.error("Failed to load events:", error);
|
|
511
766
|
}
|
|
767
|
+
}
|
|
512
768
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
769
|
+
// Load metrics
|
|
770
|
+
async function loadMetrics() {
|
|
771
|
+
try {
|
|
772
|
+
const response = await fetch("/metrics");
|
|
773
|
+
const metrics = await response.json();
|
|
518
774
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
775
|
+
document.getElementById("health-ratelimit").textContent = metrics.messages?.sent || 0;
|
|
776
|
+
} catch (error) {
|
|
777
|
+
console.error("Failed to load metrics:", error);
|
|
523
778
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Message sending
|
|
782
|
+
function setupMessageSending() {
|
|
783
|
+
document
|
|
784
|
+
.getElementById("send-message-btn")
|
|
785
|
+
.addEventListener("click", () => sendMessage(false));
|
|
786
|
+
document.getElementById("send-wait-btn").addEventListener("click", () => sendMessage(true));
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async function sendMessage(waitForResponse) {
|
|
790
|
+
const input = document.getElementById("message-input");
|
|
791
|
+
const roomSelect = document.getElementById("room-select");
|
|
792
|
+
const content = input.value.trim();
|
|
793
|
+
const room = roomSelect.value;
|
|
794
|
+
|
|
795
|
+
if (!content) {
|
|
796
|
+
addEventItem("Please enter a message", "yellow");
|
|
797
|
+
return;
|
|
529
798
|
}
|
|
530
799
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const room = roomSelect.value;
|
|
800
|
+
if (!room) {
|
|
801
|
+
addEventItem("Please select a room", "yellow");
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
536
804
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
805
|
+
try {
|
|
806
|
+
const response = await fetch("/api/message", {
|
|
807
|
+
method: "POST",
|
|
808
|
+
headers: { "Content-Type": "application/json" },
|
|
809
|
+
body: JSON.stringify({ content, room, waitForResponse })
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
const result = await response.json();
|
|
813
|
+
|
|
814
|
+
if (response.ok) {
|
|
815
|
+
input.value = "";
|
|
816
|
+
addEventItem(
|
|
817
|
+
`Sent to ${room}: ${content.substring(0, 50)}${content.length > 50 ? "..." : ""}`,
|
|
818
|
+
"green"
|
|
819
|
+
);
|
|
820
|
+
setTimeout(loadMessages, 500);
|
|
821
|
+
} else {
|
|
822
|
+
addEventItem(`Failed to send: ${result.error}`, "red");
|
|
823
|
+
}
|
|
824
|
+
} catch (error) {
|
|
825
|
+
console.error("Send error:", error);
|
|
826
|
+
addEventItem("Error sending message", "red");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
541
829
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
830
|
+
// Room management
|
|
831
|
+
function setupRoomManagement() {
|
|
832
|
+
document.getElementById("subscribe-room-btn").addEventListener("click", async () => {
|
|
833
|
+
const input = document.getElementById("room-id-input");
|
|
834
|
+
const roomId = input.value.trim();
|
|
546
835
|
|
|
547
|
-
|
|
548
|
-
const response = await fetch('/api/message', {
|
|
549
|
-
method: 'POST',
|
|
550
|
-
headers: { 'Content-Type': 'application/json' },
|
|
551
|
-
body: JSON.stringify({ content, room, waitForResponse })
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
const result = await response.json();
|
|
555
|
-
|
|
556
|
-
if (response.ok) {
|
|
557
|
-
input.value = '';
|
|
558
|
-
addEventItem(`Sent to ${room}: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`, 'green');
|
|
559
|
-
setTimeout(loadMessages, 500);
|
|
560
|
-
} else {
|
|
561
|
-
addEventItem(`Failed to send: ${result.error}`, 'red');
|
|
562
|
-
}
|
|
563
|
-
} catch (error) {
|
|
564
|
-
console.error('Send error:', error);
|
|
565
|
-
addEventItem('Error sending message', 'red');
|
|
566
|
-
}
|
|
567
|
-
}
|
|
836
|
+
if (!roomId) return;
|
|
568
837
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (!roomId) return;
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
const response = await fetch('/api/room/join', {
|
|
579
|
-
method: 'POST',
|
|
580
|
-
headers: { 'Content-Type': 'application/json' },
|
|
581
|
-
body: JSON.stringify({ roomId })
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
if (response.ok) {
|
|
585
|
-
input.value = '';
|
|
586
|
-
addEventItem(`Subscribing to room: ${roomId}`, 'green');
|
|
587
|
-
setTimeout(loadRooms, 500);
|
|
588
|
-
}
|
|
589
|
-
} catch (error) {
|
|
590
|
-
console.error('Subscribe room error:', error);
|
|
591
|
-
}
|
|
838
|
+
try {
|
|
839
|
+
const response = await fetch("/api/room/join", {
|
|
840
|
+
method: "POST",
|
|
841
|
+
headers: { "Content-Type": "application/json" },
|
|
842
|
+
body: JSON.stringify({ roomId })
|
|
592
843
|
});
|
|
593
|
-
}
|
|
594
844
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
headers: { 'Content-Type': 'application/json' },
|
|
600
|
-
body: JSON.stringify({ roomId })
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
if (response.ok) {
|
|
604
|
-
addEventItem(`Unsubscribing from room: ${roomId}`, 'yellow');
|
|
605
|
-
setTimeout(loadRooms, 500);
|
|
606
|
-
}
|
|
607
|
-
} catch (error) {
|
|
608
|
-
console.error('Unsubscribe room error:', error);
|
|
845
|
+
if (response.ok) {
|
|
846
|
+
input.value = "";
|
|
847
|
+
addEventItem(`Subscribing to room: ${roomId}`, "green");
|
|
848
|
+
setTimeout(loadRooms, 500);
|
|
609
849
|
}
|
|
850
|
+
} catch (error) {
|
|
851
|
+
console.error("Subscribe room error:", error);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async function unsubscribeRoom(roomId) {
|
|
857
|
+
try {
|
|
858
|
+
const response = await fetch("/api/room/leave", {
|
|
859
|
+
method: "POST",
|
|
860
|
+
headers: { "Content-Type": "application/json" },
|
|
861
|
+
body: JSON.stringify({ roomId })
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
if (response.ok) {
|
|
865
|
+
addEventItem(`Unsubscribing from room: ${roomId}`, "yellow");
|
|
866
|
+
setTimeout(loadRooms, 500);
|
|
867
|
+
}
|
|
868
|
+
} catch (error) {
|
|
869
|
+
console.error("Unsubscribe room error:", error);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Update status displays
|
|
874
|
+
function updateConnectionStatus(status) {
|
|
875
|
+
connectionStatus = status;
|
|
876
|
+
const dot = document.getElementById("connection-dot");
|
|
877
|
+
const text = document.getElementById("connection-status");
|
|
878
|
+
const sendBtn = document.getElementById("send-message-btn");
|
|
879
|
+
const sendWaitBtn = document.getElementById("send-wait-btn");
|
|
880
|
+
const roomSelect = document.getElementById("room-select");
|
|
881
|
+
|
|
882
|
+
if (status === "connected") {
|
|
883
|
+
dot.className = "status-dot w-3 h-3 rounded-full bg-green-500";
|
|
884
|
+
text.textContent = "Connected";
|
|
885
|
+
text.className = "text-2xl font-bold text-green-500";
|
|
886
|
+
// Only enable if there are rooms available
|
|
887
|
+
if (roomSelect.value) {
|
|
888
|
+
sendBtn.disabled = false;
|
|
889
|
+
sendWaitBtn.disabled = false;
|
|
890
|
+
}
|
|
891
|
+
} else if (status === "reconnecting") {
|
|
892
|
+
dot.className = "status-dot w-3 h-3 rounded-full bg-yellow-500";
|
|
893
|
+
text.textContent = "Reconnecting";
|
|
894
|
+
text.className = "text-2xl font-bold text-yellow-500";
|
|
895
|
+
sendBtn.disabled = true;
|
|
896
|
+
sendWaitBtn.disabled = true;
|
|
897
|
+
} else {
|
|
898
|
+
dot.className = "status-dot w-3 h-3 rounded-full bg-red-500";
|
|
899
|
+
text.textContent = "Disconnected";
|
|
900
|
+
text.className = "text-2xl font-bold text-red-500";
|
|
901
|
+
sendBtn.disabled = true;
|
|
902
|
+
sendWaitBtn.disabled = true;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
function updateAuthStatus(status) {
|
|
907
|
+
authStatus = status;
|
|
908
|
+
const dot = document.getElementById("auth-dot");
|
|
909
|
+
const text = document.getElementById("auth-status");
|
|
910
|
+
|
|
911
|
+
if (status === "success") {
|
|
912
|
+
dot.className = "status-dot w-3 h-3 rounded-full bg-green-500";
|
|
913
|
+
text.textContent = "Authenticated";
|
|
914
|
+
text.className = "text-2xl font-bold text-green-500";
|
|
915
|
+
} else {
|
|
916
|
+
dot.className = "status-dot w-3 h-3 rounded-full bg-yellow-500";
|
|
917
|
+
text.textContent = "Pending";
|
|
918
|
+
text.className = "text-2xl font-bold text-yellow-500";
|
|
610
919
|
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Health check
|
|
923
|
+
function startHealthCheck() {
|
|
924
|
+
setInterval(async () => {
|
|
925
|
+
try {
|
|
926
|
+
const response = await fetch("/health");
|
|
927
|
+
const health = await response.json();
|
|
928
|
+
|
|
929
|
+
document.getElementById("health-overall").textContent = health.status;
|
|
930
|
+
document.getElementById("health-overall").className =
|
|
931
|
+
health.status === "healthy" ? "text-green-500" : "text-yellow-500";
|
|
932
|
+
document.getElementById("health-circuit").textContent =
|
|
933
|
+
health.webhook?.circuitState || "N/A";
|
|
934
|
+
document.getElementById("health-uptime").textContent = formatUptime(health.timestamp);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
console.error("Health check failed:", error);
|
|
937
|
+
}
|
|
938
|
+
}, 5000);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function formatUptime(timestamp) {
|
|
942
|
+
const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
|
|
943
|
+
const hours = Math.floor(seconds / 3600);
|
|
944
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
945
|
+
return `${hours}h ${minutes}m`;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
function addEventItem(message, color) {
|
|
949
|
+
const list = document.getElementById("events-list");
|
|
950
|
+
const colorClass =
|
|
951
|
+
{
|
|
952
|
+
blue: "border-blue-500",
|
|
953
|
+
green: "border-green-500",
|
|
954
|
+
red: "border-red-500",
|
|
955
|
+
yellow: "border-yellow-500",
|
|
956
|
+
purple: "border-purple-500"
|
|
957
|
+
}[color] || "border-gray-500";
|
|
958
|
+
|
|
959
|
+
const item = document.createElement("div");
|
|
960
|
+
item.className = `event-item bg-gray-700 rounded p-2 border-l-4 ${colorClass}`;
|
|
961
|
+
item.innerHTML = `
|
|
962
|
+
<div class="flex items-center justify-between">
|
|
963
|
+
<span class="text-sm">${message}</span>
|
|
964
|
+
<span class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</span>
|
|
965
|
+
</div>
|
|
966
|
+
`;
|
|
611
967
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
connectionStatus = status;
|
|
615
|
-
const dot = document.getElementById('connection-dot');
|
|
616
|
-
const text = document.getElementById('connection-status');
|
|
617
|
-
const sendBtn = document.getElementById('send-message-btn');
|
|
618
|
-
const sendWaitBtn = document.getElementById('send-wait-btn');
|
|
619
|
-
const roomSelect = document.getElementById('room-select');
|
|
620
|
-
|
|
621
|
-
if (status === 'connected') {
|
|
622
|
-
dot.className = 'status-dot w-3 h-3 rounded-full bg-green-500';
|
|
623
|
-
text.textContent = 'Connected';
|
|
624
|
-
text.className = 'text-2xl font-bold text-green-500';
|
|
625
|
-
// Only enable if there are rooms available
|
|
626
|
-
if (roomSelect.value) {
|
|
627
|
-
sendBtn.disabled = false;
|
|
628
|
-
sendWaitBtn.disabled = false;
|
|
629
|
-
}
|
|
630
|
-
} else if (status === 'reconnecting') {
|
|
631
|
-
dot.className = 'status-dot w-3 h-3 rounded-full bg-yellow-500';
|
|
632
|
-
text.textContent = 'Reconnecting';
|
|
633
|
-
text.className = 'text-2xl font-bold text-yellow-500';
|
|
634
|
-
sendBtn.disabled = true;
|
|
635
|
-
sendWaitBtn.disabled = true;
|
|
636
|
-
} else {
|
|
637
|
-
dot.className = 'status-dot w-3 h-3 rounded-full bg-red-500';
|
|
638
|
-
text.textContent = 'Disconnected';
|
|
639
|
-
text.className = 'text-2xl font-bold text-red-500';
|
|
640
|
-
sendBtn.disabled = true;
|
|
641
|
-
sendWaitBtn.disabled = true;
|
|
642
|
-
}
|
|
968
|
+
if (list.firstChild && list.firstChild.textContent.includes("Waiting for events")) {
|
|
969
|
+
list.innerHTML = "";
|
|
643
970
|
}
|
|
644
971
|
|
|
645
|
-
|
|
646
|
-
authStatus = status;
|
|
647
|
-
const dot = document.getElementById('auth-dot');
|
|
648
|
-
const text = document.getElementById('auth-status');
|
|
972
|
+
list.insertBefore(item, list.firstChild);
|
|
649
973
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
text.textContent = 'Authenticated';
|
|
653
|
-
text.className = 'text-2xl font-bold text-green-500';
|
|
654
|
-
} else {
|
|
655
|
-
dot.className = 'status-dot w-3 h-3 rounded-full bg-yellow-500';
|
|
656
|
-
text.textContent = 'Pending';
|
|
657
|
-
text.className = 'text-2xl font-bold text-yellow-500';
|
|
658
|
-
}
|
|
974
|
+
if (list.children.length > 50) {
|
|
975
|
+
list.removeChild(list.lastChild);
|
|
659
976
|
}
|
|
977
|
+
}
|
|
660
978
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
document.getElementById('health-overall').className = health.status === 'healthy' ? 'text-green-500' : 'text-yellow-500';
|
|
670
|
-
document.getElementById('health-circuit').textContent = health.webhook?.circuitState || 'N/A';
|
|
671
|
-
document.getElementById('health-uptime').textContent = formatUptime(health.timestamp);
|
|
672
|
-
} catch (error) {
|
|
673
|
-
console.error('Health check failed:', error);
|
|
674
|
-
}
|
|
675
|
-
}, 5000);
|
|
676
|
-
}
|
|
979
|
+
// ===========================================
|
|
980
|
+
// V2.0 ROOM MANAGEMENT FUNCTIONS
|
|
981
|
+
// ===========================================
|
|
982
|
+
|
|
983
|
+
async function loadRoomLimitV2() {
|
|
984
|
+
try {
|
|
985
|
+
const response = await fetch("/api/v2/rooms/limit");
|
|
986
|
+
const data = await response.json();
|
|
677
987
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
return `${hours}h ${minutes}m`;
|
|
988
|
+
const limitText = `${data.count}/${data.limit} ${data.canCreate ? "✅" : "⚠️"}`;
|
|
989
|
+
document.getElementById("room-limit-text").textContent = limitText;
|
|
990
|
+
} catch (error) {
|
|
991
|
+
console.error("Failed to load room limit:", error);
|
|
683
992
|
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
async function loadPrivateRoomsV2() {
|
|
996
|
+
try {
|
|
997
|
+
// Fetch both owned and shared rooms
|
|
998
|
+
const [ownedResponse, sharedResponse] = await Promise.all([
|
|
999
|
+
fetch("/api/v2/rooms/owned"),
|
|
1000
|
+
fetch("/api/v2/rooms/shared")
|
|
1001
|
+
]);
|
|
1002
|
+
|
|
1003
|
+
const ownedRooms = await ownedResponse.json();
|
|
1004
|
+
const sharedRooms = await sharedResponse.json();
|
|
1005
|
+
|
|
1006
|
+
// Filter to only show private rooms (exclude public rooms)
|
|
1007
|
+
// Owned rooms should already be private, but filter shared rooms too
|
|
1008
|
+
const allPrivateRooms = [
|
|
1009
|
+
...ownedRooms.filter((r) => !r.is_public),
|
|
1010
|
+
...sharedRooms.filter((r) => !r.is_public)
|
|
1011
|
+
];
|
|
1012
|
+
|
|
1013
|
+
const list = document.getElementById("private-rooms-list");
|
|
1014
|
+
if (allPrivateRooms.length === 0) {
|
|
1015
|
+
list.innerHTML = '<p class="text-gray-500">No private rooms</p>';
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
list.innerHTML = allPrivateRooms
|
|
1020
|
+
.map(
|
|
1021
|
+
(room) => `
|
|
1022
|
+
<div class="bg-gray-800 rounded p-3 border border-gray-600">
|
|
1023
|
+
<div class="flex justify-between items-start">
|
|
1024
|
+
<div class="flex-1">
|
|
1025
|
+
<div class="font-semibold">${room.name}</div>
|
|
1026
|
+
<div class="text-xs text-gray-400">${room.id}</div>
|
|
1027
|
+
${room.description ? `<div class="text-sm text-gray-400 mt-1">${room.description}</div>` : ""}
|
|
1028
|
+
<div class="text-xs mt-1">
|
|
1029
|
+
<span class="px-2 py-1 rounded bg-purple-900">🔒 Private</span>
|
|
1030
|
+
${room.is_owner ? '<span class="ml-2 px-2 py-1 rounded bg-yellow-900">👑 Owner</span>' : '<span class="ml-2 px-2 py-1 rounded bg-gray-700">🤝 Shared</span>'}
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
${
|
|
1034
|
+
room.is_owner
|
|
1035
|
+
? `
|
|
1036
|
+
<div class="flex gap-2">
|
|
1037
|
+
<button onclick="deleteRoomV2('${room.id}')" class="bg-red-600 hover:bg-red-700 text-white text-xs py-1 px-2 rounded transition">
|
|
1038
|
+
Delete
|
|
1039
|
+
</button>
|
|
1040
|
+
</div>
|
|
1041
|
+
`
|
|
1042
|
+
: ""
|
|
1043
|
+
}
|
|
1044
|
+
</div>
|
|
1045
|
+
</div>
|
|
1046
|
+
`
|
|
1047
|
+
)
|
|
1048
|
+
.join("");
|
|
1049
|
+
|
|
1050
|
+
// Also populate agent-room selector with owned rooms only
|
|
1051
|
+
populateAgentRoomSelector(ownedRooms);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
console.error("Failed to load private rooms:", error);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function setupRoomManagementV2() {
|
|
1058
|
+
document.getElementById("create-room-btn").addEventListener("click", async () => {
|
|
1059
|
+
const name = document.getElementById("new-room-name").value.trim();
|
|
1060
|
+
const description = document.getElementById("new-room-desc").value.trim();
|
|
1061
|
+
|
|
1062
|
+
if (!name) {
|
|
1063
|
+
addEventItem("Room name is required", "yellow");
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
try {
|
|
1068
|
+
const response = await fetch("/api/v2/rooms", {
|
|
1069
|
+
method: "POST",
|
|
1070
|
+
headers: { "Content-Type": "application/json" },
|
|
1071
|
+
body: JSON.stringify({ name, description: description || undefined })
|
|
1072
|
+
});
|
|
684
1073
|
|
|
685
|
-
|
|
686
|
-
const list = document.getElementById('events-list');
|
|
687
|
-
const colorClass = {
|
|
688
|
-
'blue': 'border-blue-500',
|
|
689
|
-
'green': 'border-green-500',
|
|
690
|
-
'red': 'border-red-500',
|
|
691
|
-
'yellow': 'border-yellow-500',
|
|
692
|
-
'purple': 'border-purple-500'
|
|
693
|
-
}[color] || 'border-gray-500';
|
|
694
|
-
|
|
695
|
-
const item = document.createElement('div');
|
|
696
|
-
item.className = `event-item bg-gray-700 rounded p-2 border-l-4 ${colorClass}`;
|
|
697
|
-
item.innerHTML = `
|
|
698
|
-
<div class="flex items-center justify-between">
|
|
699
|
-
<span class="text-sm">${message}</span>
|
|
700
|
-
<span class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</span>
|
|
701
|
-
</div>
|
|
702
|
-
`;
|
|
1074
|
+
const result = await response.json();
|
|
703
1075
|
|
|
704
|
-
if (
|
|
705
|
-
|
|
1076
|
+
if (response.ok) {
|
|
1077
|
+
document.getElementById("new-room-name").value = "";
|
|
1078
|
+
document.getElementById("new-room-desc").value = "";
|
|
1079
|
+
|
|
1080
|
+
addEventItem(`Created room: ${result.name}`, "green");
|
|
1081
|
+
setTimeout(() => {
|
|
1082
|
+
loadRoomLimitV2();
|
|
1083
|
+
loadPrivateRoomsV2();
|
|
1084
|
+
}, 500);
|
|
1085
|
+
} else {
|
|
1086
|
+
addEventItem(`Failed to create room: ${result.error}`, "red");
|
|
706
1087
|
}
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
console.error("Create room error:", error);
|
|
1090
|
+
addEventItem("Error creating room", "red");
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
async function deleteRoomV2(roomId) {
|
|
1096
|
+
if (!confirm("Are you sure you want to delete this room?")) return;
|
|
1097
|
+
|
|
1098
|
+
try {
|
|
1099
|
+
const response = await fetch(`/api/v2/rooms/${roomId}`, {
|
|
1100
|
+
method: "DELETE"
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
const result = await response.json();
|
|
1104
|
+
|
|
1105
|
+
if (response.ok) {
|
|
1106
|
+
addEventItem(`Deleted room: ${roomId}`, "green");
|
|
1107
|
+
setTimeout(() => {
|
|
1108
|
+
loadRoomLimitV2();
|
|
1109
|
+
loadPrivateRoomsV2();
|
|
1110
|
+
}, 500);
|
|
1111
|
+
} else {
|
|
1112
|
+
addEventItem(`Failed to delete room: ${result.error}`, "red");
|
|
1113
|
+
}
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
console.error("Delete room error:", error);
|
|
1116
|
+
addEventItem("Error deleting room", "red");
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
707
1119
|
|
|
708
|
-
|
|
1120
|
+
// ===========================================
|
|
1121
|
+
// V2.0 AGENT-ROOM MANAGEMENT FUNCTIONS
|
|
1122
|
+
// ===========================================
|
|
709
1123
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1124
|
+
function populateAgentRoomSelector(ownedRooms) {
|
|
1125
|
+
const select = document.getElementById("agent-room-select");
|
|
1126
|
+
if (ownedRooms.length === 0) {
|
|
1127
|
+
select.innerHTML = '<option value="">No owned rooms - create one first</option>';
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
select.innerHTML =
|
|
1132
|
+
'<option value="">Select a room...</option>' +
|
|
1133
|
+
ownedRooms.map((room) => `<option value="${room.id}">${room.name}</option>`).join("");
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function setupAgentRoomManagement() {
|
|
1137
|
+
document.getElementById("agent-room-select").addEventListener("change", async (e) => {
|
|
1138
|
+
const roomId = e.target.value;
|
|
1139
|
+
if (!roomId) {
|
|
1140
|
+
document.getElementById("room-agents-list").innerHTML =
|
|
1141
|
+
'<p class="text-gray-500">Select a room first</p>';
|
|
1142
|
+
document.getElementById("available-agents-list").innerHTML =
|
|
1143
|
+
'<p class="text-gray-500">Select a room first</p>';
|
|
1144
|
+
document.getElementById("room-agents-count").textContent = "-";
|
|
1145
|
+
document.getElementById("available-agents-count").textContent = "-";
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
await Promise.all([loadRoomAgentsV2(roomId), loadAvailableAgentsV2(roomId)]);
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
async function loadRoomAgentsV2(roomId) {
|
|
1154
|
+
try {
|
|
1155
|
+
const response = await fetch(`/api/v2/rooms/${roomId}/agents`);
|
|
1156
|
+
const data = await response.json();
|
|
1157
|
+
|
|
1158
|
+
document.getElementById("room-agents-count").textContent = `${data.count} agents`;
|
|
1159
|
+
|
|
1160
|
+
const list = document.getElementById("room-agents-list");
|
|
1161
|
+
if (data.agents.length === 0) {
|
|
1162
|
+
list.innerHTML = '<p class="text-gray-500">No agents in this room</p>';
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
list.innerHTML = data.agents
|
|
1167
|
+
.map(
|
|
1168
|
+
(agent) => `
|
|
1169
|
+
<div class="bg-gray-800 rounded p-3 border border-gray-600 flex justify-between items-center">
|
|
1170
|
+
<div>
|
|
1171
|
+
<div class="font-semibold">${agent.agent_name || "Unnamed"}</div>
|
|
1172
|
+
<div class="text-xs text-gray-400">${agent.agent_id}</div>
|
|
1173
|
+
${
|
|
1174
|
+
agent.capabilities
|
|
1175
|
+
? `
|
|
1176
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
|
1177
|
+
${agent.capabilities
|
|
1178
|
+
.slice(0, 3)
|
|
1179
|
+
.map(
|
|
1180
|
+
(cap) =>
|
|
1181
|
+
`<span class="text-xs px-2 py-1 bg-blue-900 rounded">${cap.name}</span>`
|
|
1182
|
+
)
|
|
1183
|
+
.join("")}
|
|
1184
|
+
</div>
|
|
1185
|
+
`
|
|
1186
|
+
: ""
|
|
1187
|
+
}
|
|
1188
|
+
</div>
|
|
1189
|
+
<button onclick="removeAgentFromRoomV2('${roomId}', '${agent.agent_id}')"
|
|
1190
|
+
class="bg-red-600 hover:bg-red-700 text-white text-xs py-1 px-3 rounded transition">
|
|
1191
|
+
Remove
|
|
1192
|
+
</button>
|
|
1193
|
+
</div>
|
|
1194
|
+
`
|
|
1195
|
+
)
|
|
1196
|
+
.join("");
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
console.error("Failed to load room agents:", error);
|
|
1199
|
+
document.getElementById("room-agents-list").innerHTML =
|
|
1200
|
+
'<p class="text-red-500">Error loading agents</p>';
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
async function loadAvailableAgentsV2(roomId) {
|
|
1205
|
+
try {
|
|
1206
|
+
const response = await fetch(`/api/v2/rooms/${roomId}/available-agents`);
|
|
1207
|
+
const data = await response.json();
|
|
1208
|
+
|
|
1209
|
+
document.getElementById("available-agents-count").textContent = `${data.count} available`;
|
|
1210
|
+
|
|
1211
|
+
const list = document.getElementById("available-agents-list");
|
|
1212
|
+
if (data.agents.length === 0) {
|
|
1213
|
+
list.innerHTML = '<p class="text-gray-500">No more agents available</p>';
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
list.innerHTML = data.agents
|
|
1218
|
+
.map(
|
|
1219
|
+
(agent) => `
|
|
1220
|
+
<div class="bg-gray-800 rounded p-3 border border-gray-600 flex justify-between items-center">
|
|
1221
|
+
<div>
|
|
1222
|
+
<div class="font-semibold">${agent.agent_name || "Unnamed"}</div>
|
|
1223
|
+
<div class="text-xs text-gray-400">${agent.agent_id}</div>
|
|
1224
|
+
${agent.description ? `<div class="text-xs text-gray-400 mt-1">${agent.description.substring(0, 60)}...</div>` : ""}
|
|
1225
|
+
</div>
|
|
1226
|
+
<button onclick="addAgentToRoomV2('${roomId}', '${agent.agent_id}')"
|
|
1227
|
+
class="bg-green-600 hover:bg-green-700 text-white text-xs py-1 px-3 rounded transition">
|
|
1228
|
+
Add
|
|
1229
|
+
</button>
|
|
1230
|
+
</div>
|
|
1231
|
+
`
|
|
1232
|
+
)
|
|
1233
|
+
.join("");
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
console.error("Failed to load available agents:", error);
|
|
1236
|
+
document.getElementById("available-agents-list").innerHTML =
|
|
1237
|
+
'<p class="text-red-500">Error loading agents</p>';
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
async function addAgentToRoomV2(roomId, agentId) {
|
|
1242
|
+
try {
|
|
1243
|
+
const response = await fetch(`/api/v2/rooms/${roomId}/agents/${agentId}`, {
|
|
1244
|
+
method: "POST"
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
const result = await response.json();
|
|
1248
|
+
|
|
1249
|
+
if (response.ok) {
|
|
1250
|
+
addEventItem(`Added agent to room`, "green");
|
|
1251
|
+
setTimeout(() => {
|
|
1252
|
+
loadRoomAgentsV2(roomId);
|
|
1253
|
+
loadAvailableAgentsV2(roomId);
|
|
1254
|
+
}, 500);
|
|
1255
|
+
} else {
|
|
1256
|
+
addEventItem(`Failed to add agent: ${result.error}`, "red");
|
|
1257
|
+
}
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
console.error("Add agent error:", error);
|
|
1260
|
+
addEventItem("Error adding agent", "red");
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
async function removeAgentFromRoomV2(roomId, agentId) {
|
|
1265
|
+
try {
|
|
1266
|
+
const response = await fetch(`/api/v2/rooms/${roomId}/agents/${agentId}`, {
|
|
1267
|
+
method: "DELETE"
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
const result = await response.json();
|
|
1271
|
+
|
|
1272
|
+
if (response.ok) {
|
|
1273
|
+
addEventItem(`Removed agent from room`, "green");
|
|
1274
|
+
setTimeout(() => {
|
|
1275
|
+
loadRoomAgentsV2(roomId);
|
|
1276
|
+
loadAvailableAgentsV2(roomId);
|
|
1277
|
+
}, 500);
|
|
1278
|
+
} else {
|
|
1279
|
+
addEventItem(`Failed to remove agent: ${result.error}`, "red");
|
|
1280
|
+
}
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
console.error("Remove agent error:", error);
|
|
1283
|
+
addEventItem("Error removing agent", "red");
|
|
713
1284
|
}
|
|
1285
|
+
}
|
|
714
1286
|
</script>
|
|
715
|
-
</body>
|
|
1287
|
+
</body>
|
|
716
1288
|
</html>
|