@rookdaemon/agora 0.3.0 → 0.4.1
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 +36 -0
- package/dist/chunk-2U4PZINT.js +453 -0
- package/dist/chunk-2U4PZINT.js.map +1 -0
- package/dist/chunk-D7Y66GFC.js +572 -0
- package/dist/chunk-D7Y66GFC.js.map +1 -0
- package/dist/{chunk-JUOGKXFN.js → chunk-IOHECZYT.js} +77 -557
- package/dist/chunk-IOHECZYT.js.map +1 -0
- package/dist/cli.js +41 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +101 -14
- package/dist/index.js +105 -423
- package/dist/index.js.map +1 -1
- package/dist/relay/relay-server.d.ts +2 -0
- package/dist/relay/relay-server.js +27 -0
- package/dist/relay/relay-server.js.map +1 -0
- package/package.json +3 -1
- package/dist/chunk-JUOGKXFN.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_BOOTSTRAP_RELAYS,
|
|
3
|
-
MessageStore,
|
|
4
3
|
PeerDiscoveryService,
|
|
5
4
|
RelayClient,
|
|
6
|
-
RelayServer,
|
|
7
5
|
ReputationStore,
|
|
8
|
-
canonicalize,
|
|
9
6
|
computeAllTrustScores,
|
|
10
|
-
computeId,
|
|
11
7
|
computeTrustScore,
|
|
12
8
|
computeTrustScores,
|
|
13
9
|
createCommit,
|
|
14
|
-
createEnvelope,
|
|
15
10
|
createReveal,
|
|
16
11
|
createVerification,
|
|
17
12
|
decay,
|
|
18
13
|
decodeInboundEnvelope,
|
|
19
|
-
exportKeyPair,
|
|
20
14
|
formatDisplayName,
|
|
21
|
-
generateKeyPair,
|
|
22
15
|
getDefaultBootstrapRelay,
|
|
23
16
|
hashPrediction,
|
|
24
|
-
importKeyPair,
|
|
25
17
|
initPeerConfig,
|
|
26
18
|
loadPeerConfig,
|
|
27
19
|
parseBootstrapRelay,
|
|
@@ -30,15 +22,33 @@ import {
|
|
|
30
22
|
sendToPeer,
|
|
31
23
|
sendViaRelay,
|
|
32
24
|
shortKey,
|
|
33
|
-
signMessage,
|
|
34
25
|
validateCommitRecord,
|
|
35
26
|
validateRevealRecord,
|
|
36
27
|
validateVerificationRecord,
|
|
37
|
-
verifyEnvelope,
|
|
38
28
|
verifyReveal,
|
|
39
|
-
verifySignature,
|
|
40
29
|
verifyVerificationSignature
|
|
41
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-IOHECZYT.js";
|
|
31
|
+
import {
|
|
32
|
+
MessageBuffer,
|
|
33
|
+
createRestRouter,
|
|
34
|
+
createToken,
|
|
35
|
+
requireAuth,
|
|
36
|
+
revokeToken,
|
|
37
|
+
runRelay
|
|
38
|
+
} from "./chunk-2U4PZINT.js";
|
|
39
|
+
import {
|
|
40
|
+
MessageStore,
|
|
41
|
+
RelayServer,
|
|
42
|
+
canonicalize,
|
|
43
|
+
computeId,
|
|
44
|
+
createEnvelope,
|
|
45
|
+
exportKeyPair,
|
|
46
|
+
generateKeyPair,
|
|
47
|
+
importKeyPair,
|
|
48
|
+
signMessage,
|
|
49
|
+
verifyEnvelope,
|
|
50
|
+
verifySignature
|
|
51
|
+
} from "./chunk-D7Y66GFC.js";
|
|
42
52
|
|
|
43
53
|
// src/registry/capability.ts
|
|
44
54
|
import { createHash } from "crypto";
|
|
@@ -495,416 +505,6 @@ async function loadAgoraConfigAsync(path) {
|
|
|
495
505
|
return parseConfig(config);
|
|
496
506
|
}
|
|
497
507
|
|
|
498
|
-
// src/relay/message-buffer.ts
|
|
499
|
-
var MAX_MESSAGES_PER_AGENT = 100;
|
|
500
|
-
var MessageBuffer = class {
|
|
501
|
-
buffers = /* @__PURE__ */ new Map();
|
|
502
|
-
/**
|
|
503
|
-
* Add a message to an agent's buffer.
|
|
504
|
-
* Evicts the oldest message if the buffer is full.
|
|
505
|
-
*/
|
|
506
|
-
add(publicKey, message) {
|
|
507
|
-
let queue = this.buffers.get(publicKey);
|
|
508
|
-
if (!queue) {
|
|
509
|
-
queue = [];
|
|
510
|
-
this.buffers.set(publicKey, queue);
|
|
511
|
-
}
|
|
512
|
-
queue.push(message);
|
|
513
|
-
if (queue.length > MAX_MESSAGES_PER_AGENT) {
|
|
514
|
-
queue.shift();
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Retrieve messages for an agent, optionally filtering by `since` timestamp.
|
|
519
|
-
* Returns messages with timestamp > since (exclusive).
|
|
520
|
-
*/
|
|
521
|
-
get(publicKey, since) {
|
|
522
|
-
const queue = this.buffers.get(publicKey) ?? [];
|
|
523
|
-
if (since === void 0) {
|
|
524
|
-
return [...queue];
|
|
525
|
-
}
|
|
526
|
-
return queue.filter((m) => m.timestamp > since);
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Clear all messages for an agent (after polling without `since`).
|
|
530
|
-
*/
|
|
531
|
-
clear(publicKey) {
|
|
532
|
-
this.buffers.set(publicKey, []);
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Remove all state for a disconnected agent.
|
|
536
|
-
*/
|
|
537
|
-
delete(publicKey) {
|
|
538
|
-
this.buffers.delete(publicKey);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
// src/relay/jwt-auth.ts
|
|
543
|
-
import jwt from "jsonwebtoken";
|
|
544
|
-
import { randomBytes } from "crypto";
|
|
545
|
-
var revokedJtis = /* @__PURE__ */ new Map();
|
|
546
|
-
function pruneExpiredRevocations() {
|
|
547
|
-
const now = Date.now();
|
|
548
|
-
for (const [jti, expiry] of revokedJtis) {
|
|
549
|
-
if (expiry <= now) {
|
|
550
|
-
revokedJtis.delete(jti);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
function getJwtSecret() {
|
|
555
|
-
const secret = process.env.AGORA_RELAY_JWT_SECRET;
|
|
556
|
-
if (!secret) {
|
|
557
|
-
throw new Error(
|
|
558
|
-
"AGORA_RELAY_JWT_SECRET environment variable is required but not set"
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
return secret;
|
|
562
|
-
}
|
|
563
|
-
function getExpirySeconds() {
|
|
564
|
-
const raw = process.env.AGORA_JWT_EXPIRY_SECONDS;
|
|
565
|
-
if (raw) {
|
|
566
|
-
const parsed = parseInt(raw, 10);
|
|
567
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
568
|
-
return parsed;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return 3600;
|
|
572
|
-
}
|
|
573
|
-
function createToken(payload) {
|
|
574
|
-
const secret = getJwtSecret();
|
|
575
|
-
const expirySeconds = getExpirySeconds();
|
|
576
|
-
const jti = `${Date.now()}-${randomBytes(16).toString("hex")}`;
|
|
577
|
-
const token = jwt.sign(
|
|
578
|
-
{ publicKey: payload.publicKey, name: payload.name, jti },
|
|
579
|
-
secret,
|
|
580
|
-
{ expiresIn: expirySeconds }
|
|
581
|
-
);
|
|
582
|
-
const expiresAt = Date.now() + expirySeconds * 1e3;
|
|
583
|
-
return { token, expiresAt };
|
|
584
|
-
}
|
|
585
|
-
function revokeToken(token) {
|
|
586
|
-
try {
|
|
587
|
-
const secret = getJwtSecret();
|
|
588
|
-
const decoded = jwt.verify(token, secret);
|
|
589
|
-
if (decoded.jti) {
|
|
590
|
-
const expiry = decoded.exp ? decoded.exp * 1e3 : Date.now();
|
|
591
|
-
revokedJtis.set(decoded.jti, expiry);
|
|
592
|
-
pruneExpiredRevocations();
|
|
593
|
-
}
|
|
594
|
-
} catch {
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
function requireAuth(req, res, next) {
|
|
598
|
-
const authHeader = req.headers.authorization;
|
|
599
|
-
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
600
|
-
res.status(401).json({ error: "Missing or malformed Authorization header" });
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
const token = authHeader.slice(7);
|
|
604
|
-
try {
|
|
605
|
-
const secret = getJwtSecret();
|
|
606
|
-
const decoded = jwt.verify(token, secret);
|
|
607
|
-
if (decoded.jti && revokedJtis.has(decoded.jti)) {
|
|
608
|
-
res.status(401).json({ error: "Token has been revoked" });
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
req.agent = { publicKey: decoded.publicKey, name: decoded.name };
|
|
612
|
-
next();
|
|
613
|
-
} catch (err) {
|
|
614
|
-
if (err instanceof jwt.TokenExpiredError) {
|
|
615
|
-
res.status(401).json({ error: "Token expired" });
|
|
616
|
-
} else {
|
|
617
|
-
res.status(401).json({ error: "Invalid token" });
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// src/relay/rest-api.ts
|
|
623
|
-
import { Router } from "express";
|
|
624
|
-
import { rateLimit } from "express-rate-limit";
|
|
625
|
-
var apiRateLimit = rateLimit({
|
|
626
|
-
windowMs: 6e4,
|
|
627
|
-
limit: 60,
|
|
628
|
-
standardHeaders: "draft-7",
|
|
629
|
-
legacyHeaders: false,
|
|
630
|
-
message: { error: "Too many requests \u2014 try again later" }
|
|
631
|
-
});
|
|
632
|
-
function pruneExpiredSessions(sessions, buffer) {
|
|
633
|
-
const now = Date.now();
|
|
634
|
-
for (const [publicKey, session] of sessions) {
|
|
635
|
-
if (session.expiresAt <= now) {
|
|
636
|
-
sessions.delete(publicKey);
|
|
637
|
-
buffer.delete(publicKey);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
function createRestRouter(relay, buffer, sessions, createEnv, verifyEnv) {
|
|
642
|
-
const router = Router();
|
|
643
|
-
router.use(apiRateLimit);
|
|
644
|
-
relay.on("message-relayed", (from, to, envelope) => {
|
|
645
|
-
if (!sessions.has(to)) return;
|
|
646
|
-
const agentMap = relay.getAgents();
|
|
647
|
-
const senderAgent = agentMap.get(from);
|
|
648
|
-
const env = envelope;
|
|
649
|
-
const msg = {
|
|
650
|
-
id: env.id,
|
|
651
|
-
from,
|
|
652
|
-
fromName: senderAgent?.name,
|
|
653
|
-
type: env.type,
|
|
654
|
-
payload: env.payload,
|
|
655
|
-
timestamp: env.timestamp,
|
|
656
|
-
inReplyTo: env.inReplyTo
|
|
657
|
-
};
|
|
658
|
-
buffer.add(to, msg);
|
|
659
|
-
});
|
|
660
|
-
router.post("/v1/register", async (req, res) => {
|
|
661
|
-
const { publicKey, privateKey, name, metadata } = req.body;
|
|
662
|
-
if (!publicKey || typeof publicKey !== "string") {
|
|
663
|
-
res.status(400).json({ error: "publicKey is required" });
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
if (!privateKey || typeof privateKey !== "string") {
|
|
667
|
-
res.status(400).json({ error: "privateKey is required" });
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
const testEnvelope = createEnv(
|
|
671
|
-
"announce",
|
|
672
|
-
publicKey,
|
|
673
|
-
privateKey,
|
|
674
|
-
{ challenge: "register" },
|
|
675
|
-
Date.now()
|
|
676
|
-
);
|
|
677
|
-
const verification = verifyEnv(testEnvelope);
|
|
678
|
-
if (!verification.valid) {
|
|
679
|
-
res.status(400).json({ error: "Key pair verification failed: " + verification.reason });
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
const { token, expiresAt } = createToken({ publicKey, name });
|
|
683
|
-
pruneExpiredSessions(sessions, buffer);
|
|
684
|
-
const session = {
|
|
685
|
-
publicKey,
|
|
686
|
-
privateKey,
|
|
687
|
-
name,
|
|
688
|
-
metadata,
|
|
689
|
-
registeredAt: Date.now(),
|
|
690
|
-
expiresAt,
|
|
691
|
-
token
|
|
692
|
-
};
|
|
693
|
-
sessions.set(publicKey, session);
|
|
694
|
-
const wsAgents = relay.getAgents();
|
|
695
|
-
const peers = [];
|
|
696
|
-
for (const agent of wsAgents.values()) {
|
|
697
|
-
if (agent.publicKey !== publicKey) {
|
|
698
|
-
peers.push({
|
|
699
|
-
publicKey: agent.publicKey,
|
|
700
|
-
name: agent.name,
|
|
701
|
-
lastSeen: agent.lastSeen
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
for (const s of sessions.values()) {
|
|
706
|
-
if (s.publicKey !== publicKey && !wsAgents.has(s.publicKey)) {
|
|
707
|
-
peers.push({
|
|
708
|
-
publicKey: s.publicKey,
|
|
709
|
-
name: s.name,
|
|
710
|
-
lastSeen: s.registeredAt
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
res.json({ token, expiresAt, peers });
|
|
715
|
-
});
|
|
716
|
-
router.post(
|
|
717
|
-
"/v1/send",
|
|
718
|
-
requireAuth,
|
|
719
|
-
async (req, res) => {
|
|
720
|
-
const { to, type, payload, inReplyTo } = req.body;
|
|
721
|
-
if (!to || typeof to !== "string") {
|
|
722
|
-
res.status(400).json({ error: "to is required" });
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
if (!type || typeof type !== "string") {
|
|
726
|
-
res.status(400).json({ error: "type is required" });
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
if (payload === void 0) {
|
|
730
|
-
res.status(400).json({ error: "payload is required" });
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
const senderPublicKey = req.agent.publicKey;
|
|
734
|
-
const session = sessions.get(senderPublicKey);
|
|
735
|
-
if (!session) {
|
|
736
|
-
res.status(401).json({ error: "Session not found \u2014 please re-register" });
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
const envelope = createEnv(
|
|
740
|
-
type,
|
|
741
|
-
senderPublicKey,
|
|
742
|
-
session.privateKey,
|
|
743
|
-
payload,
|
|
744
|
-
Date.now(),
|
|
745
|
-
inReplyTo
|
|
746
|
-
);
|
|
747
|
-
const wsAgents = relay.getAgents();
|
|
748
|
-
const wsRecipient = wsAgents.get(to);
|
|
749
|
-
if (wsRecipient && wsRecipient.socket) {
|
|
750
|
-
const ws = wsRecipient.socket;
|
|
751
|
-
const OPEN = 1;
|
|
752
|
-
if (ws.readyState !== OPEN) {
|
|
753
|
-
res.status(503).json({ error: "Recipient connection is not open" });
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
756
|
-
try {
|
|
757
|
-
const relayMsg = JSON.stringify({
|
|
758
|
-
type: "message",
|
|
759
|
-
from: senderPublicKey,
|
|
760
|
-
name: session.name,
|
|
761
|
-
envelope
|
|
762
|
-
});
|
|
763
|
-
ws.send(relayMsg);
|
|
764
|
-
res.json({ ok: true, envelopeId: envelope.id });
|
|
765
|
-
return;
|
|
766
|
-
} catch (err) {
|
|
767
|
-
res.status(500).json({
|
|
768
|
-
error: "Failed to deliver message: " + (err instanceof Error ? err.message : String(err))
|
|
769
|
-
});
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
const restRecipient = sessions.get(to);
|
|
774
|
-
if (restRecipient) {
|
|
775
|
-
const senderAgent = wsAgents.get(senderPublicKey);
|
|
776
|
-
const msg = {
|
|
777
|
-
id: envelope.id,
|
|
778
|
-
from: senderPublicKey,
|
|
779
|
-
fromName: session.name ?? senderAgent?.name,
|
|
780
|
-
type: envelope.type,
|
|
781
|
-
payload: envelope.payload,
|
|
782
|
-
timestamp: envelope.timestamp,
|
|
783
|
-
inReplyTo: envelope.inReplyTo
|
|
784
|
-
};
|
|
785
|
-
buffer.add(to, msg);
|
|
786
|
-
res.json({ ok: true, envelopeId: envelope.id });
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
res.status(404).json({ error: "Recipient not connected" });
|
|
790
|
-
}
|
|
791
|
-
);
|
|
792
|
-
router.get(
|
|
793
|
-
"/v1/peers",
|
|
794
|
-
requireAuth,
|
|
795
|
-
(req, res) => {
|
|
796
|
-
const callerPublicKey = req.agent.publicKey;
|
|
797
|
-
const wsAgents = relay.getAgents();
|
|
798
|
-
const peerList = [];
|
|
799
|
-
for (const agent of wsAgents.values()) {
|
|
800
|
-
if (agent.publicKey !== callerPublicKey) {
|
|
801
|
-
peerList.push({
|
|
802
|
-
publicKey: agent.publicKey,
|
|
803
|
-
name: agent.name,
|
|
804
|
-
lastSeen: agent.lastSeen,
|
|
805
|
-
metadata: agent.metadata
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
for (const s of sessions.values()) {
|
|
810
|
-
if (s.publicKey !== callerPublicKey && !wsAgents.has(s.publicKey)) {
|
|
811
|
-
peerList.push({
|
|
812
|
-
publicKey: s.publicKey,
|
|
813
|
-
name: s.name,
|
|
814
|
-
lastSeen: s.registeredAt,
|
|
815
|
-
metadata: s.metadata
|
|
816
|
-
});
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
res.json({ peers: peerList });
|
|
820
|
-
}
|
|
821
|
-
);
|
|
822
|
-
router.get(
|
|
823
|
-
"/v1/messages",
|
|
824
|
-
requireAuth,
|
|
825
|
-
(req, res) => {
|
|
826
|
-
const publicKey = req.agent.publicKey;
|
|
827
|
-
const sinceRaw = req.query.since;
|
|
828
|
-
const limitRaw = req.query.limit;
|
|
829
|
-
const since = sinceRaw ? parseInt(sinceRaw, 10) : void 0;
|
|
830
|
-
const limit = Math.min(limitRaw ? parseInt(limitRaw, 10) : 50, 100);
|
|
831
|
-
let messages = buffer.get(publicKey, since);
|
|
832
|
-
const hasMore = messages.length > limit;
|
|
833
|
-
if (hasMore) {
|
|
834
|
-
messages = messages.slice(0, limit);
|
|
835
|
-
}
|
|
836
|
-
if (since === void 0) {
|
|
837
|
-
buffer.clear(publicKey);
|
|
838
|
-
}
|
|
839
|
-
res.json({ messages, hasMore });
|
|
840
|
-
}
|
|
841
|
-
);
|
|
842
|
-
router.delete(
|
|
843
|
-
"/v1/disconnect",
|
|
844
|
-
requireAuth,
|
|
845
|
-
(req, res) => {
|
|
846
|
-
const publicKey = req.agent.publicKey;
|
|
847
|
-
const authHeader = req.headers.authorization;
|
|
848
|
-
const token = authHeader.slice(7);
|
|
849
|
-
revokeToken(token);
|
|
850
|
-
sessions.delete(publicKey);
|
|
851
|
-
buffer.delete(publicKey);
|
|
852
|
-
res.json({ ok: true });
|
|
853
|
-
}
|
|
854
|
-
);
|
|
855
|
-
return router;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// src/relay/run-relay.ts
|
|
859
|
-
import http from "http";
|
|
860
|
-
import express from "express";
|
|
861
|
-
var createEnvelopeForRest = (type, sender, privateKey, payload, timestamp, inReplyTo) => createEnvelope(
|
|
862
|
-
type,
|
|
863
|
-
sender,
|
|
864
|
-
privateKey,
|
|
865
|
-
payload,
|
|
866
|
-
timestamp ?? Date.now(),
|
|
867
|
-
inReplyTo
|
|
868
|
-
);
|
|
869
|
-
async function runRelay(options = {}) {
|
|
870
|
-
const wsPort = options.wsPort ?? parseInt(process.env.PORT ?? "3001", 10);
|
|
871
|
-
const enableRest = options.enableRest ?? (typeof process.env.AGORA_RELAY_JWT_SECRET === "string" && process.env.AGORA_RELAY_JWT_SECRET.length > 0);
|
|
872
|
-
const relay = new RelayServer(options.relayOptions);
|
|
873
|
-
await relay.start(wsPort);
|
|
874
|
-
if (!enableRest) {
|
|
875
|
-
return { relay };
|
|
876
|
-
}
|
|
877
|
-
if (!process.env.AGORA_RELAY_JWT_SECRET) {
|
|
878
|
-
await relay.stop();
|
|
879
|
-
throw new Error(
|
|
880
|
-
"AGORA_RELAY_JWT_SECRET environment variable is required when REST API is enabled"
|
|
881
|
-
);
|
|
882
|
-
}
|
|
883
|
-
const restPort = options.restPort ?? wsPort + 1;
|
|
884
|
-
const messageBuffer = new MessageBuffer();
|
|
885
|
-
const restSessions = /* @__PURE__ */ new Map();
|
|
886
|
-
const app = express();
|
|
887
|
-
app.use(express.json());
|
|
888
|
-
const verifyForRest = (envelope) => verifyEnvelope(envelope);
|
|
889
|
-
const router = createRestRouter(
|
|
890
|
-
relay,
|
|
891
|
-
messageBuffer,
|
|
892
|
-
restSessions,
|
|
893
|
-
createEnvelopeForRest,
|
|
894
|
-
verifyForRest
|
|
895
|
-
);
|
|
896
|
-
app.use(router);
|
|
897
|
-
app.use((_req, res) => {
|
|
898
|
-
res.status(404).json({ error: "Not found" });
|
|
899
|
-
});
|
|
900
|
-
const httpServer = http.createServer(app);
|
|
901
|
-
await new Promise((resolve2, reject) => {
|
|
902
|
-
httpServer.listen(restPort, () => resolve2());
|
|
903
|
-
httpServer.on("error", reject);
|
|
904
|
-
});
|
|
905
|
-
return { relay, httpServer };
|
|
906
|
-
}
|
|
907
|
-
|
|
908
508
|
// src/service.ts
|
|
909
509
|
var AgoraService = class {
|
|
910
510
|
config;
|
|
@@ -931,7 +531,7 @@ var AgoraService = class {
|
|
|
931
531
|
error: `Unknown peer: ${options.peerName}`
|
|
932
532
|
};
|
|
933
533
|
}
|
|
934
|
-
if (peer.url) {
|
|
534
|
+
if (peer.url && !options.relayOnly) {
|
|
935
535
|
const transportConfig = {
|
|
936
536
|
identity: {
|
|
937
537
|
publicKey: this.config.identity.publicKey,
|
|
@@ -950,6 +550,19 @@ var AgoraService = class {
|
|
|
950
550
|
return httpResult;
|
|
951
551
|
}
|
|
952
552
|
this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);
|
|
553
|
+
if (options.direct) {
|
|
554
|
+
return {
|
|
555
|
+
ok: false,
|
|
556
|
+
status: httpResult.status,
|
|
557
|
+
error: `Direct send to ${options.peerName} failed: ${httpResult.error}`
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
} else if (options.direct && !peer.url) {
|
|
561
|
+
return {
|
|
562
|
+
ok: false,
|
|
563
|
+
status: 0,
|
|
564
|
+
error: `Direct send failed: peer '${options.peerName}' has no URL configured`
|
|
565
|
+
};
|
|
953
566
|
}
|
|
954
567
|
if (this.relayClient?.connected() && this.config.relay) {
|
|
955
568
|
const relayResult = await sendViaRelay(
|
|
@@ -1076,6 +689,73 @@ var AgoraService = class {
|
|
|
1076
689
|
};
|
|
1077
690
|
}
|
|
1078
691
|
};
|
|
692
|
+
|
|
693
|
+
// src/reputation/network.ts
|
|
694
|
+
var MAX_VERIFICATIONS_IN_RESPONSE = 50;
|
|
695
|
+
async function handleReputationQuery(query, store, currentTime) {
|
|
696
|
+
const allVerifications = await store.getVerifications();
|
|
697
|
+
let relevantVerifications = allVerifications.filter((v) => v.target === query.agent);
|
|
698
|
+
if (query.domain !== void 0) {
|
|
699
|
+
relevantVerifications = relevantVerifications.filter((v) => v.domain === query.domain);
|
|
700
|
+
}
|
|
701
|
+
if (query.after !== void 0) {
|
|
702
|
+
const after = query.after;
|
|
703
|
+
relevantVerifications = relevantVerifications.filter((v) => v.timestamp > after);
|
|
704
|
+
}
|
|
705
|
+
let scores;
|
|
706
|
+
if (query.domain !== void 0) {
|
|
707
|
+
const score = computeTrustScore(query.agent, query.domain, allVerifications, currentTime);
|
|
708
|
+
scores = { [query.domain]: score };
|
|
709
|
+
} else {
|
|
710
|
+
const scoreMap = computeTrustScores(query.agent, allVerifications, currentTime);
|
|
711
|
+
scores = {};
|
|
712
|
+
for (const [domain, score] of scoreMap.entries()) {
|
|
713
|
+
scores[domain] = score;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const limitedVerifications = relevantVerifications.slice().sort((a, b) => b.timestamp - a.timestamp).slice(0, MAX_VERIFICATIONS_IN_RESPONSE);
|
|
717
|
+
const response = {
|
|
718
|
+
agent: query.agent,
|
|
719
|
+
verifications: limitedVerifications,
|
|
720
|
+
scores
|
|
721
|
+
};
|
|
722
|
+
if (query.domain !== void 0) {
|
|
723
|
+
response.domain = query.domain;
|
|
724
|
+
}
|
|
725
|
+
return response;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/reputation/sync.ts
|
|
729
|
+
async function syncReputationFromPeer(agentPublicKey, domain, store, sendMessage) {
|
|
730
|
+
const query = {
|
|
731
|
+
agent: agentPublicKey,
|
|
732
|
+
domain
|
|
733
|
+
};
|
|
734
|
+
const response = await sendMessage("reputation_query", query);
|
|
735
|
+
const existing = await store.getVerifications();
|
|
736
|
+
const existingIds = new Set(existing.map((v) => v.id));
|
|
737
|
+
let added = 0;
|
|
738
|
+
let skipped = 0;
|
|
739
|
+
for (const record of response.verifications) {
|
|
740
|
+
if (existingIds.has(record.id)) {
|
|
741
|
+
skipped++;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
const sigResult = verifyVerificationSignature(record);
|
|
745
|
+
if (!sigResult.valid) {
|
|
746
|
+
skipped++;
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (record.domain !== domain) {
|
|
750
|
+
skipped++;
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
await store.addVerification(record);
|
|
754
|
+
existingIds.add(record.id);
|
|
755
|
+
added++;
|
|
756
|
+
}
|
|
757
|
+
return { added, skipped };
|
|
758
|
+
}
|
|
1079
759
|
export {
|
|
1080
760
|
AgoraService,
|
|
1081
761
|
DEFAULT_BOOTSTRAP_RELAYS,
|
|
@@ -1106,6 +786,7 @@ export {
|
|
|
1106
786
|
generateKeyPair,
|
|
1107
787
|
getDefaultBootstrapRelay,
|
|
1108
788
|
getDefaultConfigPath,
|
|
789
|
+
handleReputationQuery,
|
|
1109
790
|
hashPrediction,
|
|
1110
791
|
importKeyPair,
|
|
1111
792
|
initPeerConfig,
|
|
@@ -1121,6 +802,7 @@ export {
|
|
|
1121
802
|
sendToPeer,
|
|
1122
803
|
shortKey,
|
|
1123
804
|
signMessage,
|
|
805
|
+
syncReputationFromPeer,
|
|
1124
806
|
validateCapability,
|
|
1125
807
|
validateCommitRecord,
|
|
1126
808
|
validatePeerListRequest,
|