agent-teams-dashboard 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.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/README.zh-TW.md +72 -0
- package/bin/agent-teams-dashboard.js +8 -0
- package/dist/assets/index-BwUrrF1R.css +1 -0
- package/dist/assets/index-DEOAkXJf.js +49 -0
- package/dist/index.html +13 -0
- package/package.json +57 -0
- package/server-dist/server/index.js +96 -0
- package/server-dist/server/teamsApi.js +68 -0
- package/server-dist/server/teamsCache.js +291 -0
- package/server-dist/server/teamsWatcher.js +128 -0
- package/server-dist/server/wsServer.js +62 -0
- package/server-dist/src/types.js +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
2
|
+
import * as cache from './teamsCache.js';
|
|
3
|
+
const HEARTBEAT_INTERVAL = 30_000;
|
|
4
|
+
const PONG_TIMEOUT = 10_000;
|
|
5
|
+
let wss = null;
|
|
6
|
+
function broadcast(event) {
|
|
7
|
+
if (!wss)
|
|
8
|
+
return;
|
|
9
|
+
const data = JSON.stringify(event);
|
|
10
|
+
for (const client of wss.clients) {
|
|
11
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
12
|
+
client.send(data);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function initWebSocket(server) {
|
|
17
|
+
wss = new WebSocketServer({ server, path: '/ws' });
|
|
18
|
+
console.log('[ws] WebSocket server attached on /ws');
|
|
19
|
+
wss.on('connection', (ws) => {
|
|
20
|
+
ws.isAlive = true;
|
|
21
|
+
const clientCount = wss.clients.size;
|
|
22
|
+
console.log(`[ws] Client connected (total: ${clientCount})`);
|
|
23
|
+
// Send initial snapshot
|
|
24
|
+
const snapshot = cache.getSnapshot();
|
|
25
|
+
ws.send(JSON.stringify({ type: 'snapshot', data: snapshot }));
|
|
26
|
+
ws.on('pong', () => {
|
|
27
|
+
ws.isAlive = true;
|
|
28
|
+
});
|
|
29
|
+
ws.on('close', () => {
|
|
30
|
+
const remaining = wss.clients.size;
|
|
31
|
+
console.log(`[ws] Client disconnected (total: ${remaining})`);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
// Heartbeat interval
|
|
35
|
+
const heartbeat = setInterval(() => {
|
|
36
|
+
if (!wss)
|
|
37
|
+
return;
|
|
38
|
+
for (const client of wss.clients) {
|
|
39
|
+
const ws = client;
|
|
40
|
+
if (!ws.isAlive) {
|
|
41
|
+
ws.terminate();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
ws.isAlive = false;
|
|
45
|
+
ws.ping();
|
|
46
|
+
// Terminate if no pong within timeout
|
|
47
|
+
const pongTimer = setTimeout(() => {
|
|
48
|
+
if (!ws.isAlive)
|
|
49
|
+
ws.terminate();
|
|
50
|
+
}, PONG_TIMEOUT);
|
|
51
|
+
ws.once('pong', () => clearTimeout(pongTimer));
|
|
52
|
+
}
|
|
53
|
+
}, HEARTBEAT_INTERVAL);
|
|
54
|
+
wss.on('close', () => {
|
|
55
|
+
clearInterval(heartbeat);
|
|
56
|
+
});
|
|
57
|
+
// Listen for cache changes and broadcast full snapshot
|
|
58
|
+
cache.onChange.on('change', () => {
|
|
59
|
+
const snap = cache.getSnapshot();
|
|
60
|
+
broadcast({ type: 'snapshot', data: snap });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|