@rookdaemon/agora 0.1.2 → 0.1.5
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/README.md +265 -1
- package/dist/cli.js +481 -36
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +44 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/message/envelope.d.ts +1 -1
- package/dist/message/envelope.d.ts.map +1 -1
- package/dist/message/envelope.js.map +1 -1
- package/dist/message/types/paper-discovery.d.ts +28 -0
- package/dist/message/types/paper-discovery.d.ts.map +1 -0
- package/dist/message/types/paper-discovery.js +2 -0
- package/dist/message/types/paper-discovery.js.map +1 -0
- package/dist/peer/client.d.ts +50 -0
- package/dist/peer/client.d.ts.map +1 -0
- package/dist/peer/client.js +138 -0
- package/dist/peer/client.js.map +1 -0
- package/dist/peer/manager.d.ts +65 -0
- package/dist/peer/manager.d.ts.map +1 -0
- package/dist/peer/manager.js +153 -0
- package/dist/peer/manager.js.map +1 -0
- package/dist/peer/server.d.ts +65 -0
- package/dist/peer/server.d.ts.map +1 -0
- package/dist/peer/server.js +154 -0
- package/dist/peer/server.js.map +1 -0
- package/dist/relay/client.d.ts +112 -0
- package/dist/relay/client.d.ts.map +1 -0
- package/dist/relay/client.js +281 -0
- package/dist/relay/client.js.map +1 -0
- package/dist/relay/server.d.ts +60 -0
- package/dist/relay/server.d.ts.map +1 -0
- package/dist/relay/server.js +266 -0
- package/dist/relay/server.js.map +1 -0
- package/dist/relay/types.d.ts +35 -0
- package/dist/relay/types.d.ts.map +1 -0
- package/dist/relay/types.js +2 -0
- package/dist/relay/types.js.map +1 -0
- package/dist/transport/peer-config.d.ts +3 -2
- package/dist/transport/peer-config.d.ts.map +1 -1
- package/dist/transport/peer-config.js.map +1 -1
- package/dist/transport/relay.d.ts +23 -0
- package/dist/transport/relay.d.ts.map +1 -0
- package/dist/transport/relay.js +85 -0
- package/dist/transport/relay.js.map +1 -0
- package/package.json +1 -38
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { verifyEnvelope } from '../message/envelope.js';
|
|
4
|
+
/**
|
|
5
|
+
* Persistent WebSocket client for the Agora relay server.
|
|
6
|
+
* Maintains a long-lived connection, handles reconnection, and routes messages.
|
|
7
|
+
*/
|
|
8
|
+
export class RelayClient extends EventEmitter {
|
|
9
|
+
ws = null;
|
|
10
|
+
config;
|
|
11
|
+
reconnectAttempts = 0;
|
|
12
|
+
reconnectTimeout = null;
|
|
13
|
+
pingInterval = null;
|
|
14
|
+
isConnected = false;
|
|
15
|
+
isRegistered = false;
|
|
16
|
+
shouldReconnect = true;
|
|
17
|
+
onlinePeers = new Map();
|
|
18
|
+
constructor(config) {
|
|
19
|
+
super();
|
|
20
|
+
this.config = {
|
|
21
|
+
pingInterval: 30000,
|
|
22
|
+
maxReconnectDelay: 60000,
|
|
23
|
+
...config,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Connect to the relay server
|
|
28
|
+
*/
|
|
29
|
+
async connect() {
|
|
30
|
+
if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.shouldReconnect = true;
|
|
34
|
+
return this.doConnect();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Disconnect from the relay server
|
|
38
|
+
*/
|
|
39
|
+
disconnect() {
|
|
40
|
+
this.shouldReconnect = false;
|
|
41
|
+
this.cleanup();
|
|
42
|
+
if (this.ws) {
|
|
43
|
+
this.ws.close();
|
|
44
|
+
this.ws = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if currently connected and registered
|
|
49
|
+
*/
|
|
50
|
+
connected() {
|
|
51
|
+
return this.isConnected && this.isRegistered;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Send a message to a specific peer
|
|
55
|
+
*/
|
|
56
|
+
async send(to, envelope) {
|
|
57
|
+
if (!this.connected()) {
|
|
58
|
+
return { ok: false, error: 'Not connected to relay' };
|
|
59
|
+
}
|
|
60
|
+
const message = {
|
|
61
|
+
type: 'message',
|
|
62
|
+
to,
|
|
63
|
+
envelope,
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
this.ws.send(JSON.stringify(message));
|
|
67
|
+
return { ok: true };
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Broadcast a message to all connected peers
|
|
75
|
+
*/
|
|
76
|
+
async broadcast(envelope) {
|
|
77
|
+
if (!this.connected()) {
|
|
78
|
+
return { ok: false, error: 'Not connected to relay' };
|
|
79
|
+
}
|
|
80
|
+
const message = {
|
|
81
|
+
type: 'broadcast',
|
|
82
|
+
envelope,
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
this.ws.send(JSON.stringify(message));
|
|
86
|
+
return { ok: true };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get list of currently online peers
|
|
94
|
+
*/
|
|
95
|
+
getOnlinePeers() {
|
|
96
|
+
return Array.from(this.onlinePeers.values());
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a specific peer is online
|
|
100
|
+
*/
|
|
101
|
+
isPeerOnline(publicKey) {
|
|
102
|
+
return this.onlinePeers.has(publicKey);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Internal: Perform connection
|
|
106
|
+
*/
|
|
107
|
+
async doConnect() {
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
try {
|
|
110
|
+
this.ws = new WebSocket(this.config.relayUrl);
|
|
111
|
+
let resolved = false;
|
|
112
|
+
const resolveOnce = (callback) => {
|
|
113
|
+
if (!resolved) {
|
|
114
|
+
resolved = true;
|
|
115
|
+
callback();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
this.ws.on('open', () => {
|
|
119
|
+
this.isConnected = true;
|
|
120
|
+
this.reconnectAttempts = 0;
|
|
121
|
+
this.startPingInterval();
|
|
122
|
+
// Send registration message
|
|
123
|
+
const registerMsg = {
|
|
124
|
+
type: 'register',
|
|
125
|
+
publicKey: this.config.publicKey,
|
|
126
|
+
name: this.config.name,
|
|
127
|
+
};
|
|
128
|
+
this.ws.send(JSON.stringify(registerMsg));
|
|
129
|
+
});
|
|
130
|
+
this.ws.on('message', (data) => {
|
|
131
|
+
try {
|
|
132
|
+
const msg = JSON.parse(data.toString());
|
|
133
|
+
this.handleMessage(msg);
|
|
134
|
+
// Resolve promise on successful registration
|
|
135
|
+
if (msg.type === 'registered' && !resolved) {
|
|
136
|
+
resolveOnce(() => resolve());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
this.emit('error', new Error(`Failed to parse message: ${err instanceof Error ? err.message : String(err)}`));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
this.ws.on('close', () => {
|
|
144
|
+
this.isConnected = false;
|
|
145
|
+
this.isRegistered = false;
|
|
146
|
+
this.cleanup();
|
|
147
|
+
this.emit('disconnected');
|
|
148
|
+
if (this.shouldReconnect) {
|
|
149
|
+
this.scheduleReconnect();
|
|
150
|
+
}
|
|
151
|
+
if (!resolved) {
|
|
152
|
+
resolveOnce(() => reject(new Error('Connection closed before registration')));
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
this.ws.on('error', (err) => {
|
|
156
|
+
this.emit('error', err);
|
|
157
|
+
if (!resolved) {
|
|
158
|
+
resolveOnce(() => reject(err));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
reject(err);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Handle incoming message from relay
|
|
169
|
+
*/
|
|
170
|
+
handleMessage(msg) {
|
|
171
|
+
switch (msg.type) {
|
|
172
|
+
case 'registered':
|
|
173
|
+
this.isRegistered = true;
|
|
174
|
+
if (msg.peers) {
|
|
175
|
+
// Populate initial peer list
|
|
176
|
+
for (const peer of msg.peers) {
|
|
177
|
+
this.onlinePeers.set(peer.publicKey, peer);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.emit('connected');
|
|
181
|
+
break;
|
|
182
|
+
case 'message':
|
|
183
|
+
if (msg.envelope && msg.from) {
|
|
184
|
+
// Verify envelope signature
|
|
185
|
+
const verification = verifyEnvelope(msg.envelope);
|
|
186
|
+
if (!verification.valid) {
|
|
187
|
+
this.emit('error', new Error(`Invalid envelope signature: ${verification.reason}`));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Verify sender matches 'from' field
|
|
191
|
+
if (msg.envelope.sender !== msg.from) {
|
|
192
|
+
this.emit('error', new Error('Envelope sender does not match relay from field'));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// Emit verified message
|
|
196
|
+
this.emit('message', msg.envelope, msg.from, msg.name);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
case 'peer_online':
|
|
200
|
+
if (msg.publicKey) {
|
|
201
|
+
const peer = {
|
|
202
|
+
publicKey: msg.publicKey,
|
|
203
|
+
name: msg.name,
|
|
204
|
+
};
|
|
205
|
+
this.onlinePeers.set(msg.publicKey, peer);
|
|
206
|
+
this.emit('peer_online', peer);
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
case 'peer_offline':
|
|
210
|
+
if (msg.publicKey) {
|
|
211
|
+
const peer = this.onlinePeers.get(msg.publicKey);
|
|
212
|
+
if (peer) {
|
|
213
|
+
this.onlinePeers.delete(msg.publicKey);
|
|
214
|
+
this.emit('peer_offline', peer);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'error':
|
|
219
|
+
this.emit('error', new Error(`Relay error: ${msg.message || 'Unknown error'}`));
|
|
220
|
+
break;
|
|
221
|
+
case 'pong':
|
|
222
|
+
// Keepalive response, no action needed
|
|
223
|
+
break;
|
|
224
|
+
default:
|
|
225
|
+
// Unknown message type, ignore
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Schedule reconnection with exponential backoff
|
|
231
|
+
*/
|
|
232
|
+
scheduleReconnect() {
|
|
233
|
+
if (this.reconnectTimeout) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s (max)
|
|
237
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), this.config.maxReconnectDelay);
|
|
238
|
+
this.reconnectAttempts++;
|
|
239
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
240
|
+
this.reconnectTimeout = null;
|
|
241
|
+
if (this.shouldReconnect) {
|
|
242
|
+
this.doConnect().catch((err) => {
|
|
243
|
+
this.emit('error', err);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}, delay);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Start periodic ping messages
|
|
250
|
+
*/
|
|
251
|
+
startPingInterval() {
|
|
252
|
+
this.stopPingInterval();
|
|
253
|
+
this.pingInterval = setInterval(() => {
|
|
254
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
255
|
+
const ping = { type: 'ping' };
|
|
256
|
+
this.ws.send(JSON.stringify(ping));
|
|
257
|
+
}
|
|
258
|
+
}, this.config.pingInterval);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Stop ping interval
|
|
262
|
+
*/
|
|
263
|
+
stopPingInterval() {
|
|
264
|
+
if (this.pingInterval) {
|
|
265
|
+
clearInterval(this.pingInterval);
|
|
266
|
+
this.pingInterval = null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Cleanup resources
|
|
271
|
+
*/
|
|
272
|
+
cleanup() {
|
|
273
|
+
this.stopPingInterval();
|
|
274
|
+
if (this.reconnectTimeout) {
|
|
275
|
+
clearTimeout(this.reconnectTimeout);
|
|
276
|
+
this.reconnectTimeout = null;
|
|
277
|
+
}
|
|
278
|
+
this.onlinePeers.clear();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
import { type Envelope } from '../message/envelope.js';
|
|
4
|
+
/**
|
|
5
|
+
* Represents a connected agent in the relay
|
|
6
|
+
*/
|
|
7
|
+
interface ConnectedAgent {
|
|
8
|
+
/** Agent's public key */
|
|
9
|
+
publicKey: string;
|
|
10
|
+
/** Optional agent name */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** WebSocket connection */
|
|
13
|
+
socket: WebSocket;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Events emitted by RelayServer
|
|
17
|
+
*/
|
|
18
|
+
export interface RelayServerEvents {
|
|
19
|
+
'agent-registered': (publicKey: string) => void;
|
|
20
|
+
'agent-disconnected': (publicKey: string) => void;
|
|
21
|
+
'message-relayed': (from: string, to: string, envelope: Envelope) => void;
|
|
22
|
+
'error': (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* WebSocket relay server for routing messages between agents.
|
|
26
|
+
*
|
|
27
|
+
* Agents connect to the relay and register with their public key.
|
|
28
|
+
* Messages are routed to recipients based on the 'to' field.
|
|
29
|
+
* All envelopes are verified before being forwarded.
|
|
30
|
+
*/
|
|
31
|
+
export declare class RelayServer extends EventEmitter {
|
|
32
|
+
private wss;
|
|
33
|
+
private agents;
|
|
34
|
+
/**
|
|
35
|
+
* Start the relay server
|
|
36
|
+
*/
|
|
37
|
+
start(port: number): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Stop the relay server
|
|
40
|
+
*/
|
|
41
|
+
stop(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Get all connected agents
|
|
44
|
+
*/
|
|
45
|
+
getAgents(): Map<string, ConnectedAgent>;
|
|
46
|
+
/**
|
|
47
|
+
* Handle incoming connection
|
|
48
|
+
*/
|
|
49
|
+
private handleConnection;
|
|
50
|
+
/**
|
|
51
|
+
* Send an error message to a client
|
|
52
|
+
*/
|
|
53
|
+
private sendError;
|
|
54
|
+
/**
|
|
55
|
+
* Broadcast a peer event to all connected agents
|
|
56
|
+
*/
|
|
57
|
+
private broadcastPeerEvent;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/relay/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAmB,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvE;;GAEG;AACH,UAAU,cAAc;IACtB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,MAAM,EAAE,SAAS,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC1E,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED;;;;;;GAMG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,MAAM,CAAqC;IAEnD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BlC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3B;;OAEG;IACH,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAIxC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+KxB;;OAEG;IACH,OAAO,CAAC,SAAS;IAWjB;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAmB3B"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
|
+
import { verifyEnvelope } from '../message/envelope.js';
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket relay server for routing messages between agents.
|
|
6
|
+
*
|
|
7
|
+
* Agents connect to the relay and register with their public key.
|
|
8
|
+
* Messages are routed to recipients based on the 'to' field.
|
|
9
|
+
* All envelopes are verified before being forwarded.
|
|
10
|
+
*/
|
|
11
|
+
export class RelayServer extends EventEmitter {
|
|
12
|
+
wss = null;
|
|
13
|
+
agents = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Start the relay server
|
|
16
|
+
*/
|
|
17
|
+
start(port) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
try {
|
|
20
|
+
this.wss = new WebSocketServer({ port });
|
|
21
|
+
let resolved = false;
|
|
22
|
+
this.wss.on('error', (error) => {
|
|
23
|
+
this.emit('error', error);
|
|
24
|
+
if (!resolved) {
|
|
25
|
+
resolved = true;
|
|
26
|
+
reject(error);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
this.wss.on('listening', () => {
|
|
30
|
+
if (!resolved) {
|
|
31
|
+
resolved = true;
|
|
32
|
+
resolve();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
this.wss.on('connection', (socket) => {
|
|
36
|
+
this.handleConnection(socket);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
reject(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Stop the relay server
|
|
46
|
+
*/
|
|
47
|
+
async stop() {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
if (!this.wss) {
|
|
50
|
+
resolve();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Close all agent connections
|
|
54
|
+
for (const agent of this.agents.values()) {
|
|
55
|
+
agent.socket.close();
|
|
56
|
+
}
|
|
57
|
+
this.agents.clear();
|
|
58
|
+
this.wss.close((err) => {
|
|
59
|
+
if (err) {
|
|
60
|
+
reject(err);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.wss = null;
|
|
64
|
+
resolve();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get all connected agents
|
|
71
|
+
*/
|
|
72
|
+
getAgents() {
|
|
73
|
+
return new Map(this.agents);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handle incoming connection
|
|
77
|
+
*/
|
|
78
|
+
handleConnection(socket) {
|
|
79
|
+
let agentPublicKey = null;
|
|
80
|
+
socket.on('message', (data) => {
|
|
81
|
+
try {
|
|
82
|
+
const msg = JSON.parse(data.toString());
|
|
83
|
+
// Handle registration
|
|
84
|
+
if (msg.type === 'register' && !agentPublicKey) {
|
|
85
|
+
if (!msg.publicKey || typeof msg.publicKey !== 'string') {
|
|
86
|
+
this.sendError(socket, 'Invalid registration: missing or invalid publicKey');
|
|
87
|
+
socket.close();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const publicKey = msg.publicKey;
|
|
91
|
+
const name = msg.name;
|
|
92
|
+
agentPublicKey = publicKey;
|
|
93
|
+
const agent = {
|
|
94
|
+
publicKey,
|
|
95
|
+
name,
|
|
96
|
+
socket,
|
|
97
|
+
};
|
|
98
|
+
this.agents.set(publicKey, agent);
|
|
99
|
+
this.emit('agent-registered', publicKey);
|
|
100
|
+
// Send registration confirmation with list of online peers
|
|
101
|
+
const peers = Array.from(this.agents.values())
|
|
102
|
+
.filter(a => a.publicKey !== publicKey)
|
|
103
|
+
.map(a => ({ publicKey: a.publicKey, name: a.name }));
|
|
104
|
+
socket.send(JSON.stringify({
|
|
105
|
+
type: 'registered',
|
|
106
|
+
publicKey,
|
|
107
|
+
peers,
|
|
108
|
+
}));
|
|
109
|
+
// Notify other agents that this agent is now online
|
|
110
|
+
this.broadcastPeerEvent('peer_online', publicKey, name);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Require registration before processing messages
|
|
114
|
+
if (!agentPublicKey) {
|
|
115
|
+
this.sendError(socket, 'Not registered: send registration message first');
|
|
116
|
+
socket.close();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Handle message relay
|
|
120
|
+
if (msg.type === 'message') {
|
|
121
|
+
if (!msg.to || typeof msg.to !== 'string') {
|
|
122
|
+
this.sendError(socket, 'Invalid message: missing or invalid "to" field');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!msg.envelope || typeof msg.envelope !== 'object') {
|
|
126
|
+
this.sendError(socket, 'Invalid message: missing or invalid "envelope" field');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const envelope = msg.envelope;
|
|
130
|
+
// Verify envelope signature
|
|
131
|
+
const verification = verifyEnvelope(envelope);
|
|
132
|
+
if (!verification.valid) {
|
|
133
|
+
this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Verify sender matches registered agent
|
|
137
|
+
if (envelope.sender !== agentPublicKey) {
|
|
138
|
+
this.sendError(socket, 'Envelope sender does not match registered public key');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// Find recipient
|
|
142
|
+
const recipient = this.agents.get(msg.to);
|
|
143
|
+
if (!recipient || recipient.socket.readyState !== WebSocket.OPEN) {
|
|
144
|
+
this.sendError(socket, 'Recipient not connected');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Forward envelope to recipient wrapped in relay message format
|
|
148
|
+
try {
|
|
149
|
+
const senderAgent = this.agents.get(agentPublicKey);
|
|
150
|
+
const relayMessage = {
|
|
151
|
+
type: 'message',
|
|
152
|
+
from: agentPublicKey,
|
|
153
|
+
name: senderAgent?.name,
|
|
154
|
+
envelope,
|
|
155
|
+
};
|
|
156
|
+
recipient.socket.send(JSON.stringify(relayMessage));
|
|
157
|
+
this.emit('message-relayed', agentPublicKey, msg.to, envelope);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
this.sendError(socket, 'Failed to relay message');
|
|
161
|
+
this.emit('error', err);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// Handle broadcast: same validation as message, then forward to all other agents
|
|
166
|
+
if (msg.type === 'broadcast') {
|
|
167
|
+
if (!msg.envelope || typeof msg.envelope !== 'object') {
|
|
168
|
+
this.sendError(socket, 'Invalid broadcast: missing or invalid "envelope" field');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const envelope = msg.envelope;
|
|
172
|
+
const verification = verifyEnvelope(envelope);
|
|
173
|
+
if (!verification.valid) {
|
|
174
|
+
this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (envelope.sender !== agentPublicKey) {
|
|
178
|
+
this.sendError(socket, 'Envelope sender does not match registered public key');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const senderAgent = this.agents.get(agentPublicKey);
|
|
182
|
+
const relayMessage = {
|
|
183
|
+
type: 'message',
|
|
184
|
+
from: agentPublicKey,
|
|
185
|
+
name: senderAgent?.name,
|
|
186
|
+
envelope,
|
|
187
|
+
};
|
|
188
|
+
const messageStr = JSON.stringify(relayMessage);
|
|
189
|
+
for (const agent of this.agents.values()) {
|
|
190
|
+
if (agent.publicKey !== agentPublicKey && agent.socket.readyState === WebSocket.OPEN) {
|
|
191
|
+
try {
|
|
192
|
+
agent.socket.send(messageStr);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
this.emit('error', err);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// Handle ping
|
|
202
|
+
if (msg.type === 'ping') {
|
|
203
|
+
socket.send(JSON.stringify({ type: 'pong' }));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// Unknown message type
|
|
207
|
+
this.sendError(socket, `Unknown message type: ${msg.type}`);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
// Invalid JSON or other parsing errors
|
|
211
|
+
this.emit('error', new Error(`Message parsing failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
212
|
+
this.sendError(socket, 'Invalid message format');
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
socket.on('close', () => {
|
|
216
|
+
if (agentPublicKey) {
|
|
217
|
+
const agent = this.agents.get(agentPublicKey);
|
|
218
|
+
const agentName = agent?.name;
|
|
219
|
+
this.agents.delete(agentPublicKey);
|
|
220
|
+
this.emit('agent-disconnected', agentPublicKey);
|
|
221
|
+
// Notify other agents that this agent went offline
|
|
222
|
+
this.broadcastPeerEvent('peer_offline', agentPublicKey, agentName);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
socket.on('error', (error) => {
|
|
226
|
+
this.emit('error', error);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Send an error message to a client
|
|
231
|
+
*/
|
|
232
|
+
sendError(socket, message) {
|
|
233
|
+
try {
|
|
234
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
235
|
+
socket.send(JSON.stringify({ type: 'error', message }));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
// Log errors when sending error messages, but don't propagate to avoid cascading failures
|
|
240
|
+
this.emit('error', new Error(`Failed to send error message: ${err instanceof Error ? err.message : String(err)}`));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Broadcast a peer event to all connected agents
|
|
245
|
+
*/
|
|
246
|
+
broadcastPeerEvent(eventType, publicKey, name) {
|
|
247
|
+
const message = {
|
|
248
|
+
type: eventType,
|
|
249
|
+
publicKey,
|
|
250
|
+
name,
|
|
251
|
+
};
|
|
252
|
+
const messageStr = JSON.stringify(message);
|
|
253
|
+
for (const agent of this.agents.values()) {
|
|
254
|
+
// Don't send the event to the agent it's about
|
|
255
|
+
if (agent.publicKey !== publicKey && agent.socket.readyState === WebSocket.OPEN) {
|
|
256
|
+
try {
|
|
257
|
+
agent.socket.send(messageStr);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
this.emit('error', new Error(`Failed to send ${eventType} event: ${err instanceof Error ? err.message : String(err)}`));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/relay/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,EAAE,cAAc,EAAiB,MAAM,wBAAwB,CAAC;AAwBvE;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,YAAY;IACnC,GAAG,GAA2B,IAAI,CAAC;IACnC,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEnD;;OAEG;IACH,KAAK,CAAC,IAAY;QAChB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC;gBACH,IAAI,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzC,IAAI,QAAQ,GAAG,KAAK,CAAC;gBAErB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBAC7B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;oBAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,QAAQ,GAAG,IAAI,CAAC;wBAChB,MAAM,CAAC,KAAK,CAAC,CAAC;oBAChB,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;oBAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,QAAQ,GAAG,IAAI,CAAC;wBAChB,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAiB,EAAE,EAAE;oBAC9C,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAChC,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,8BAA8B;YAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;gBACzC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAEpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACrB,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;oBAChB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,MAAiB;QACxC,IAAI,cAAc,GAAkB,IAAI,CAAC;QAEzC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;YACpC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAExC,sBAAsB;gBACtB,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,cAAc,EAAE,CAAC;oBAC/C,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;wBACxD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,oDAAoD,CAAC,CAAC;wBAC7E,MAAM,CAAC,KAAK,EAAE,CAAC;wBACf,OAAO;oBACT,CAAC;oBAED,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;oBAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;oBACtB,cAAc,GAAG,SAAS,CAAC;oBAC3B,MAAM,KAAK,GAAmB;wBAC5B,SAAS;wBACT,IAAI;wBACJ,MAAM;qBACP,CAAC;oBAEF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;oBAClC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;oBAEzC,2DAA2D;oBAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;yBAC3C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC;yBACtC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;oBAExD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;wBACzB,IAAI,EAAE,YAAY;wBAClB,SAAS;wBACT,KAAK;qBACN,CAAC,CAAC,CAAC;oBAEJ,oDAAoD;oBACpD,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;oBACxD,OAAO;gBACT,CAAC;gBAED,kDAAkD;gBAClD,IAAI,CAAC,cAAc,EAAE,CAAC;oBACpB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,iDAAiD,CAAC,CAAC;oBAC1E,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,OAAO;gBACT,CAAC;gBAED,uBAAuB;gBACvB,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;wBAC1C,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,gDAAgD,CAAC,CAAC;wBACzE,OAAO;oBACT,CAAC;oBAED,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;wBACtD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,sDAAsD,CAAC,CAAC;wBAC/E,OAAO;oBACT,CAAC;oBAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAoB,CAAC;oBAE1C,4BAA4B;oBAC5B,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;oBAC9C,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;wBACxB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,qBAAqB,YAAY,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC,CAAC;wBAC5F,OAAO;oBACT,CAAC;oBAED,yCAAyC;oBACzC,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;wBACvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,sDAAsD,CAAC,CAAC;wBAC/E,OAAO;oBACT,CAAC;oBAED,iBAAiB;oBACjB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAC1C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;wBACjE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;wBAClD,OAAO;oBACT,CAAC;oBAED,gEAAgE;oBAChE,IAAI,CAAC;wBACH,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;wBACpD,MAAM,YAAY,GAAG;4BACnB,IAAI,EAAE,SAAS;4BACf,IAAI,EAAE,cAAc;4BACpB,IAAI,EAAE,WAAW,EAAE,IAAI;4BACvB,QAAQ;yBACT,CAAC;wBACF,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;wBACpD,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;oBACjE,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;wBAClD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAY,CAAC,CAAC;oBACnC,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,iFAAiF;gBACjF,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC7B,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;wBACtD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,wDAAwD,CAAC,CAAC;wBACjF,OAAO;oBACT,CAAC;oBAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAoB,CAAC;oBAE1C,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;oBAC9C,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;wBACxB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,qBAAqB,YAAY,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC,CAAC;wBAC5F,OAAO;oBACT,CAAC;oBAED,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;wBACvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,sDAAsD,CAAC,CAAC;wBAC/E,OAAO;oBACT,CAAC;oBAED,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;oBACpD,MAAM,YAAY,GAAG;wBACnB,IAAI,EAAE,SAAkB;wBACxB,IAAI,EAAE,cAAc;wBACpB,IAAI,EAAE,WAAW,EAAE,IAAI;wBACvB,QAAQ;qBACT,CAAC;oBACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;oBAEhD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;wBACzC,IAAI,KAAK,CAAC,SAAS,KAAK,cAAc,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;4BACrF,IAAI,CAAC;gCACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;4BAChC,CAAC;4BAAC,OAAO,GAAG,EAAE,CAAC;gCACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAY,CAAC,CAAC;4BACnC,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,cAAc;gBACd,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;oBAC9C,OAAO;gBACT,CAAC;gBAED,uBAAuB;gBACvB,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,yBAAyB,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,uCAAuC;gBACvC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,2BAA2B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7G,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAC9C,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,CAAC;gBAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,cAAc,CAAC,CAAC;gBAEhD,mDAAmD;gBACnD,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;YACrE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,MAAiB,EAAE,OAAe;QAClD,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,0FAA0F;YAC1F,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACrH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,SAAyC,EAAE,SAAiB,EAAE,IAAa;QACpG,MAAM,OAAO,GAAG;YACd,IAAI,EAAE,SAAS;YACf,SAAS;YACT,IAAI;SACL,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAE3C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;YACzC,+CAA+C;YAC/C,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBAChF,IAAI,CAAC;oBACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAChC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,kBAAkB,SAAS,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1H,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Envelope } from '../message/envelope.js';
|
|
2
|
+
/**
|
|
3
|
+
* Messages sent from client to relay server
|
|
4
|
+
*/
|
|
5
|
+
export interface RelayClientMessage {
|
|
6
|
+
type: 'register' | 'message' | 'broadcast' | 'ping';
|
|
7
|
+
publicKey?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
to?: string;
|
|
10
|
+
envelope?: Envelope;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Messages received from relay server
|
|
14
|
+
*/
|
|
15
|
+
export interface RelayServerMessage {
|
|
16
|
+
type: 'registered' | 'message' | 'error' | 'pong' | 'peer_online' | 'peer_offline';
|
|
17
|
+
publicKey?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
from?: string;
|
|
20
|
+
envelope?: Envelope;
|
|
21
|
+
peers?: Array<{
|
|
22
|
+
publicKey: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
}>;
|
|
25
|
+
code?: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Peer presence information
|
|
30
|
+
*/
|
|
31
|
+
export interface RelayPeer {
|
|
32
|
+
publicKey: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|