@rookdaemon/agora 0.1.2 → 0.2.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/README.md +457 -1
- package/dist/cli.js +627 -37
- 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/discovery/bootstrap.d.ts +32 -0
- package/dist/discovery/bootstrap.d.ts.map +1 -0
- package/dist/discovery/bootstrap.js +36 -0
- package/dist/discovery/bootstrap.js.map +1 -0
- package/dist/discovery/peer-discovery.d.ts +59 -0
- package/dist/discovery/peer-discovery.d.ts.map +1 -0
- package/dist/discovery/peer-discovery.js +108 -0
- package/dist/discovery/peer-discovery.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -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/message/types/peer-discovery.d.ts +78 -0
- package/dist/message/types/peer-discovery.d.ts.map +1 -0
- package/dist/message/types/peer-discovery.js +90 -0
- package/dist/message/types/peer-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/registry/discovery-service.d.ts +64 -0
- package/dist/registry/discovery-service.d.ts.map +1 -0
- package/dist/registry/discovery-service.js +129 -0
- package/dist/registry/discovery-service.js.map +1 -0
- package/dist/registry/messages.d.ts +55 -0
- package/dist/registry/messages.d.ts.map +1 -1
- 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 +76 -0
- package/dist/relay/server.d.ts.map +1 -0
- package/dist/relay/server.js +338 -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 +7 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer discovery message types for the Agora network.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Request peer list from relay
|
|
6
|
+
*/
|
|
7
|
+
export interface PeerListRequestPayload {
|
|
8
|
+
/** Optional filters */
|
|
9
|
+
filters?: {
|
|
10
|
+
/** Only peers seen in last N ms */
|
|
11
|
+
activeWithin?: number;
|
|
12
|
+
/** Maximum peers to return */
|
|
13
|
+
limit?: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Relay responds with connected peers
|
|
18
|
+
*/
|
|
19
|
+
export interface PeerListResponsePayload {
|
|
20
|
+
/** List of known peers */
|
|
21
|
+
peers: Array<{
|
|
22
|
+
/** Peer's Ed25519 public key */
|
|
23
|
+
publicKey: string;
|
|
24
|
+
/** Optional metadata (if peer announced) */
|
|
25
|
+
metadata?: {
|
|
26
|
+
name?: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
capabilities?: string[];
|
|
29
|
+
};
|
|
30
|
+
/** Last seen timestamp (ms) */
|
|
31
|
+
lastSeen: number;
|
|
32
|
+
}>;
|
|
33
|
+
/** Total peer count (may be > peers.length if limited) */
|
|
34
|
+
totalPeers: number;
|
|
35
|
+
/** Relay's public key (for trust verification) */
|
|
36
|
+
relayPublicKey: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Agent recommends another agent
|
|
40
|
+
*/
|
|
41
|
+
export interface PeerReferralPayload {
|
|
42
|
+
/** Referred peer's public key */
|
|
43
|
+
publicKey: string;
|
|
44
|
+
/** Optional endpoint (if known) */
|
|
45
|
+
endpoint?: string;
|
|
46
|
+
/** Optional metadata */
|
|
47
|
+
metadata?: {
|
|
48
|
+
name?: string;
|
|
49
|
+
version?: string;
|
|
50
|
+
capabilities?: string[];
|
|
51
|
+
};
|
|
52
|
+
/** Referrer's comment */
|
|
53
|
+
comment?: string;
|
|
54
|
+
/** Trust hint (RFC-001 integration) */
|
|
55
|
+
trustScore?: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate PeerListRequestPayload
|
|
59
|
+
*/
|
|
60
|
+
export declare function validatePeerListRequest(payload: unknown): {
|
|
61
|
+
valid: boolean;
|
|
62
|
+
errors: string[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Validate PeerListResponsePayload
|
|
66
|
+
*/
|
|
67
|
+
export declare function validatePeerListResponse(payload: unknown): {
|
|
68
|
+
valid: boolean;
|
|
69
|
+
errors: string[];
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Validate PeerReferralPayload
|
|
73
|
+
*/
|
|
74
|
+
export declare function validatePeerReferral(payload: unknown): {
|
|
75
|
+
valid: boolean;
|
|
76
|
+
errors: string[];
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=peer-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-discovery.d.ts","sourceRoot":"","sources":["../../../src/message/types/peer-discovery.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,uBAAuB;IACvB,OAAO,CAAC,EAAE;QACR,mCAAmC;QACnC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,8BAA8B;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,KAAK,EAAE,KAAK,CAAC;QACX,gCAAgC;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,4CAA4C;QAC5C,QAAQ,CAAC,EAAE;YACT,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;SACzB,CAAC;QACF,+BAA+B;QAC/B,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC;IACF,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAyB9F;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAqC/F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA2B3F"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer discovery message types for the Agora network.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validate PeerListRequestPayload
|
|
6
|
+
*/
|
|
7
|
+
export function validatePeerListRequest(payload) {
|
|
8
|
+
const errors = [];
|
|
9
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
10
|
+
errors.push('Payload must be an object');
|
|
11
|
+
return { valid: false, errors };
|
|
12
|
+
}
|
|
13
|
+
const p = payload;
|
|
14
|
+
if (p.filters !== undefined) {
|
|
15
|
+
if (typeof p.filters !== 'object' || p.filters === null) {
|
|
16
|
+
errors.push('filters must be an object');
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const filters = p.filters;
|
|
20
|
+
if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {
|
|
21
|
+
errors.push('filters.activeWithin must be a number');
|
|
22
|
+
}
|
|
23
|
+
if (filters.limit !== undefined && typeof filters.limit !== 'number') {
|
|
24
|
+
errors.push('filters.limit must be a number');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { valid: errors.length === 0, errors };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate PeerListResponsePayload
|
|
32
|
+
*/
|
|
33
|
+
export function validatePeerListResponse(payload) {
|
|
34
|
+
const errors = [];
|
|
35
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
36
|
+
errors.push('Payload must be an object');
|
|
37
|
+
return { valid: false, errors };
|
|
38
|
+
}
|
|
39
|
+
const p = payload;
|
|
40
|
+
if (!Array.isArray(p.peers)) {
|
|
41
|
+
errors.push('peers must be an array');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
p.peers.forEach((peer, index) => {
|
|
45
|
+
if (typeof peer !== 'object' || peer === null) {
|
|
46
|
+
errors.push(`peers[${index}] must be an object`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const peerObj = peer;
|
|
50
|
+
if (typeof peerObj.publicKey !== 'string') {
|
|
51
|
+
errors.push(`peers[${index}].publicKey must be a string`);
|
|
52
|
+
}
|
|
53
|
+
if (typeof peerObj.lastSeen !== 'number') {
|
|
54
|
+
errors.push(`peers[${index}].lastSeen must be a number`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (typeof p.totalPeers !== 'number') {
|
|
59
|
+
errors.push('totalPeers must be a number');
|
|
60
|
+
}
|
|
61
|
+
if (typeof p.relayPublicKey !== 'string') {
|
|
62
|
+
errors.push('relayPublicKey must be a string');
|
|
63
|
+
}
|
|
64
|
+
return { valid: errors.length === 0, errors };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate PeerReferralPayload
|
|
68
|
+
*/
|
|
69
|
+
export function validatePeerReferral(payload) {
|
|
70
|
+
const errors = [];
|
|
71
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
72
|
+
errors.push('Payload must be an object');
|
|
73
|
+
return { valid: false, errors };
|
|
74
|
+
}
|
|
75
|
+
const p = payload;
|
|
76
|
+
if (typeof p.publicKey !== 'string') {
|
|
77
|
+
errors.push('publicKey must be a string');
|
|
78
|
+
}
|
|
79
|
+
if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {
|
|
80
|
+
errors.push('endpoint must be a string');
|
|
81
|
+
}
|
|
82
|
+
if (p.comment !== undefined && typeof p.comment !== 'string') {
|
|
83
|
+
errors.push('comment must be a string');
|
|
84
|
+
}
|
|
85
|
+
if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {
|
|
86
|
+
errors.push('trustScore must be a number');
|
|
87
|
+
}
|
|
88
|
+
return { valid: errors.length === 0, errors };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=peer-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-discovery.js","sourceRoot":"","sources":["../../../src/message/types/peer-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { KeyPair } from '../identity/keypair.js';
|
|
3
|
+
import type { Envelope } from '../message/envelope.js';
|
|
4
|
+
import type { AnnouncePayload } from '../registry/messages.js';
|
|
5
|
+
/**
|
|
6
|
+
* Events emitted by PeerClient
|
|
7
|
+
*/
|
|
8
|
+
export interface PeerClientEvents {
|
|
9
|
+
'connected': (publicKey: string) => void;
|
|
10
|
+
'disconnected': () => void;
|
|
11
|
+
'message-received': (envelope: Envelope) => void;
|
|
12
|
+
'error': (error: Error) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* WebSocket client for connecting to peers
|
|
16
|
+
*/
|
|
17
|
+
export declare class PeerClient extends EventEmitter {
|
|
18
|
+
private socket;
|
|
19
|
+
private identity;
|
|
20
|
+
private announcePayload;
|
|
21
|
+
private url;
|
|
22
|
+
private peerPublicKey;
|
|
23
|
+
private reconnectAttempts;
|
|
24
|
+
private maxReconnectAttempts;
|
|
25
|
+
private baseReconnectDelay;
|
|
26
|
+
private reconnectTimeout;
|
|
27
|
+
private shouldReconnect;
|
|
28
|
+
constructor(url: string, identity: KeyPair, announcePayload: AnnouncePayload);
|
|
29
|
+
/**
|
|
30
|
+
* Connect to the peer
|
|
31
|
+
*/
|
|
32
|
+
connect(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Disconnect from the peer
|
|
35
|
+
*/
|
|
36
|
+
disconnect(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Send a message to the peer
|
|
39
|
+
*/
|
|
40
|
+
send(envelope: Envelope): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Check if connected to peer
|
|
43
|
+
*/
|
|
44
|
+
isConnected(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Get the peer's public key (if connected)
|
|
47
|
+
*/
|
|
48
|
+
getPeerPublicKey(): string | null;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/peer/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,eAAe,CAAQ;gBAEnB,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe;IAO5E;;OAEG;IACH,OAAO,IAAI,IAAI;IAqFf;;OAEG;IACH,UAAU,IAAI,IAAI;IAgBlB;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAcjC;;OAEG;IACH,WAAW,IAAI,OAAO;IAMtB;;OAEG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;CAGlC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { createEnvelope, verifyEnvelope } from '../message/envelope.js';
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket client for connecting to peers
|
|
6
|
+
*/
|
|
7
|
+
export class PeerClient extends EventEmitter {
|
|
8
|
+
socket = null;
|
|
9
|
+
identity;
|
|
10
|
+
announcePayload;
|
|
11
|
+
url;
|
|
12
|
+
peerPublicKey = null;
|
|
13
|
+
reconnectAttempts = 0;
|
|
14
|
+
maxReconnectAttempts = 10;
|
|
15
|
+
baseReconnectDelay = 1000; // 1 second
|
|
16
|
+
reconnectTimeout = null;
|
|
17
|
+
shouldReconnect = true;
|
|
18
|
+
constructor(url, identity, announcePayload) {
|
|
19
|
+
super();
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.identity = identity;
|
|
22
|
+
this.announcePayload = announcePayload;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Connect to the peer
|
|
26
|
+
*/
|
|
27
|
+
connect() {
|
|
28
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
this.socket = new WebSocket(this.url);
|
|
33
|
+
this.socket.on('open', () => {
|
|
34
|
+
this.reconnectAttempts = 0;
|
|
35
|
+
// Send announce message immediately
|
|
36
|
+
const announceEnvelope = createEnvelope('announce', this.identity.publicKey, this.identity.privateKey, this.announcePayload);
|
|
37
|
+
this.socket.send(JSON.stringify(announceEnvelope));
|
|
38
|
+
});
|
|
39
|
+
this.socket.on('message', (data) => {
|
|
40
|
+
try {
|
|
41
|
+
const envelope = JSON.parse(data.toString());
|
|
42
|
+
// Verify envelope signature
|
|
43
|
+
const verification = verifyEnvelope(envelope);
|
|
44
|
+
if (!verification.valid) {
|
|
45
|
+
// Drop invalid messages
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// First message should be an announce from the peer
|
|
49
|
+
if (!this.peerPublicKey) {
|
|
50
|
+
if (envelope.type === 'announce') {
|
|
51
|
+
this.peerPublicKey = envelope.sender;
|
|
52
|
+
this.emit('connected', this.peerPublicKey);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Verify the message is from the announced peer
|
|
57
|
+
if (envelope.sender !== this.peerPublicKey) {
|
|
58
|
+
// Drop messages from wrong sender
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Emit message-received event
|
|
62
|
+
this.emit('message-received', envelope);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Invalid JSON or other parsing errors - drop the message
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
this.socket.on('close', () => {
|
|
69
|
+
const wasConnected = this.peerPublicKey !== null;
|
|
70
|
+
this.peerPublicKey = null;
|
|
71
|
+
if (wasConnected) {
|
|
72
|
+
this.emit('disconnected');
|
|
73
|
+
}
|
|
74
|
+
// Attempt to reconnect with exponential backoff
|
|
75
|
+
if (this.shouldReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
76
|
+
this.reconnectAttempts++;
|
|
77
|
+
// Calculate delay with exponential backoff, capped at 30 seconds
|
|
78
|
+
const delay = Math.min(this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts - 1), 30000);
|
|
79
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
80
|
+
this.connect();
|
|
81
|
+
}, delay);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
this.socket.on('error', (error) => {
|
|
85
|
+
this.emit('error', error);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.emit('error', error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Disconnect from the peer
|
|
94
|
+
*/
|
|
95
|
+
disconnect() {
|
|
96
|
+
this.shouldReconnect = false;
|
|
97
|
+
if (this.reconnectTimeout) {
|
|
98
|
+
clearTimeout(this.reconnectTimeout);
|
|
99
|
+
this.reconnectTimeout = null;
|
|
100
|
+
}
|
|
101
|
+
if (this.socket) {
|
|
102
|
+
this.socket.close();
|
|
103
|
+
this.socket = null;
|
|
104
|
+
}
|
|
105
|
+
this.peerPublicKey = null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Send a message to the peer
|
|
109
|
+
*/
|
|
110
|
+
send(envelope) {
|
|
111
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
this.socket.send(JSON.stringify(envelope));
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
this.emit('error', error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if connected to peer
|
|
125
|
+
*/
|
|
126
|
+
isConnected() {
|
|
127
|
+
return this.socket !== null &&
|
|
128
|
+
this.socket.readyState === WebSocket.OPEN &&
|
|
129
|
+
this.peerPublicKey !== null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get the peer's public key (if connected)
|
|
133
|
+
*/
|
|
134
|
+
getPeerPublicKey() {
|
|
135
|
+
return this.peerPublicKey;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/peer/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { KeyPair } from '../identity/keypair.js';
|
|
3
|
+
import type { Envelope } from '../message/envelope.js';
|
|
4
|
+
import type { AnnouncePayload } from '../registry/messages.js';
|
|
5
|
+
/**
|
|
6
|
+
* Peer information with public key
|
|
7
|
+
*/
|
|
8
|
+
export interface PeerInfo {
|
|
9
|
+
publicKey: string;
|
|
10
|
+
metadata?: {
|
|
11
|
+
name?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Events emitted by PeerManager
|
|
17
|
+
*/
|
|
18
|
+
export interface PeerManagerEvents {
|
|
19
|
+
'peer-connected': (publicKey: string) => void;
|
|
20
|
+
'peer-disconnected': (publicKey: string) => void;
|
|
21
|
+
'message-received': (envelope: Envelope, fromPublicKey: string) => void;
|
|
22
|
+
'error': (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Manages both server (incoming connections) and client (outbound connections)
|
|
26
|
+
*/
|
|
27
|
+
export declare class PeerManager extends EventEmitter {
|
|
28
|
+
private server;
|
|
29
|
+
private clients;
|
|
30
|
+
private identity;
|
|
31
|
+
private announcePayload;
|
|
32
|
+
constructor(identity: KeyPair, announcePayload: AnnouncePayload);
|
|
33
|
+
/**
|
|
34
|
+
* Start listening for incoming peer connections
|
|
35
|
+
* @param port - Port to listen on
|
|
36
|
+
*/
|
|
37
|
+
start(port: number): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Stop the server and disconnect all clients
|
|
40
|
+
*/
|
|
41
|
+
stop(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Connect to a peer at the given URL
|
|
44
|
+
* @param url - WebSocket URL of the peer (e.g., ws://localhost:8080)
|
|
45
|
+
*/
|
|
46
|
+
connect(url: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Broadcast a message to all connected peers (both incoming and outgoing)
|
|
49
|
+
* @param envelope - The envelope to broadcast
|
|
50
|
+
*/
|
|
51
|
+
broadcast(envelope: Envelope): void;
|
|
52
|
+
/**
|
|
53
|
+
* Get list of all connected peers with their public keys
|
|
54
|
+
* @returns Array of peer information
|
|
55
|
+
*/
|
|
56
|
+
getPeers(): PeerInfo[];
|
|
57
|
+
/**
|
|
58
|
+
* Send a message to a specific peer by public key
|
|
59
|
+
* @param publicKey - The peer's public key
|
|
60
|
+
* @param envelope - The envelope to send
|
|
61
|
+
* @returns true if sent successfully, false otherwise
|
|
62
|
+
*/
|
|
63
|
+
send(publicKey: string, envelope: Envelope): boolean;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/peer/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,eAAe,CAAkB;gBAE7B,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe;IAM/D;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BxC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAmC1B;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAcnC;;;OAGG;IACH,QAAQ,IAAI,QAAQ,EAAE;IA6BtB;;;;;OAKG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO;CAerD"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { PeerServer } from './server.js';
|
|
3
|
+
import { PeerClient } from './client.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manages both server (incoming connections) and client (outbound connections)
|
|
6
|
+
*/
|
|
7
|
+
export class PeerManager extends EventEmitter {
|
|
8
|
+
server = null;
|
|
9
|
+
clients = new Map();
|
|
10
|
+
identity;
|
|
11
|
+
announcePayload;
|
|
12
|
+
constructor(identity, announcePayload) {
|
|
13
|
+
super();
|
|
14
|
+
this.identity = identity;
|
|
15
|
+
this.announcePayload = announcePayload;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Start listening for incoming peer connections
|
|
19
|
+
* @param port - Port to listen on
|
|
20
|
+
*/
|
|
21
|
+
async start(port) {
|
|
22
|
+
if (this.server) {
|
|
23
|
+
throw new Error('Server already started');
|
|
24
|
+
}
|
|
25
|
+
this.server = new PeerServer(this.identity, this.announcePayload);
|
|
26
|
+
// Forward server events
|
|
27
|
+
this.server.on('peer-connected', (publicKey, _peer) => {
|
|
28
|
+
this.emit('peer-connected', publicKey);
|
|
29
|
+
});
|
|
30
|
+
this.server.on('peer-disconnected', (publicKey) => {
|
|
31
|
+
this.emit('peer-disconnected', publicKey);
|
|
32
|
+
});
|
|
33
|
+
this.server.on('message-received', (envelope, fromPublicKey) => {
|
|
34
|
+
this.emit('message-received', envelope, fromPublicKey);
|
|
35
|
+
});
|
|
36
|
+
this.server.on('error', (error) => {
|
|
37
|
+
this.emit('error', error);
|
|
38
|
+
});
|
|
39
|
+
await this.server.start(port);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Stop the server and disconnect all clients
|
|
43
|
+
*/
|
|
44
|
+
async stop() {
|
|
45
|
+
// Disconnect all clients
|
|
46
|
+
for (const client of this.clients.values()) {
|
|
47
|
+
client.disconnect();
|
|
48
|
+
}
|
|
49
|
+
this.clients.clear();
|
|
50
|
+
// Stop server
|
|
51
|
+
if (this.server) {
|
|
52
|
+
await this.server.stop();
|
|
53
|
+
this.server = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Connect to a peer at the given URL
|
|
58
|
+
* @param url - WebSocket URL of the peer (e.g., ws://localhost:8080)
|
|
59
|
+
*/
|
|
60
|
+
connect(url) {
|
|
61
|
+
// Check if already connected to this URL
|
|
62
|
+
if (this.clients.has(url)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const client = new PeerClient(url, this.identity, this.announcePayload);
|
|
66
|
+
// Forward client events
|
|
67
|
+
client.on('connected', (publicKey) => {
|
|
68
|
+
this.emit('peer-connected', publicKey);
|
|
69
|
+
});
|
|
70
|
+
client.on('disconnected', () => {
|
|
71
|
+
const publicKey = client.getPeerPublicKey();
|
|
72
|
+
if (publicKey) {
|
|
73
|
+
this.emit('peer-disconnected', publicKey);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
client.on('message-received', (envelope) => {
|
|
77
|
+
const publicKey = client.getPeerPublicKey();
|
|
78
|
+
if (publicKey) {
|
|
79
|
+
this.emit('message-received', envelope, publicKey);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
client.on('error', (error) => {
|
|
83
|
+
this.emit('error', error);
|
|
84
|
+
});
|
|
85
|
+
this.clients.set(url, client);
|
|
86
|
+
client.connect();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Broadcast a message to all connected peers (both incoming and outgoing)
|
|
90
|
+
* @param envelope - The envelope to broadcast
|
|
91
|
+
*/
|
|
92
|
+
broadcast(envelope) {
|
|
93
|
+
// Broadcast to server peers
|
|
94
|
+
if (this.server) {
|
|
95
|
+
this.server.broadcast(envelope);
|
|
96
|
+
}
|
|
97
|
+
// Broadcast to client peers
|
|
98
|
+
for (const client of this.clients.values()) {
|
|
99
|
+
if (client.isConnected()) {
|
|
100
|
+
client.send(envelope);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get list of all connected peers with their public keys
|
|
106
|
+
* @returns Array of peer information
|
|
107
|
+
*/
|
|
108
|
+
getPeers() {
|
|
109
|
+
const peers = [];
|
|
110
|
+
// Get server peers
|
|
111
|
+
if (this.server) {
|
|
112
|
+
for (const [publicKey, peer] of this.server.getPeers()) {
|
|
113
|
+
peers.push({
|
|
114
|
+
publicKey,
|
|
115
|
+
metadata: peer.metadata,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Get client peers
|
|
120
|
+
for (const client of this.clients.values()) {
|
|
121
|
+
if (client.isConnected()) {
|
|
122
|
+
const publicKey = client.getPeerPublicKey();
|
|
123
|
+
if (publicKey) {
|
|
124
|
+
// Avoid duplicates (same peer might be connected via both server and client)
|
|
125
|
+
if (!peers.find(p => p.publicKey === publicKey)) {
|
|
126
|
+
peers.push({ publicKey });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return peers;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Send a message to a specific peer by public key
|
|
135
|
+
* @param publicKey - The peer's public key
|
|
136
|
+
* @param envelope - The envelope to send
|
|
137
|
+
* @returns true if sent successfully, false otherwise
|
|
138
|
+
*/
|
|
139
|
+
send(publicKey, envelope) {
|
|
140
|
+
// Try to send via server
|
|
141
|
+
if (this.server && this.server.send(publicKey, envelope)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
// Try to send via clients
|
|
145
|
+
for (const client of this.clients.values()) {
|
|
146
|
+
if (client.getPeerPublicKey() === publicKey && client.isConnected()) {
|
|
147
|
+
return client.send(envelope);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/peer/manager.ts"],"names":[],"mappings":"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"}
|