open-agents-nexus 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/ARCHITECTURE.md +2104 -0
- package/LICENSE +28 -0
- package/README.md +198 -0
- package/dist/chat/index.d.ts +24 -0
- package/dist/chat/index.js +56 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/messages.d.ts +28 -0
- package/dist/chat/messages.js +33 -0
- package/dist/chat/messages.js.map +1 -0
- package/dist/chat/room.d.ts +49 -0
- package/dist/chat/room.js +123 -0
- package/dist/chat/room.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +222 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/dht/index.d.ts +16 -0
- package/dist/dht/index.js +33 -0
- package/dist/dht/index.js.map +1 -0
- package/dist/dht/registry.d.ts +24 -0
- package/dist/dht/registry.js +103 -0
- package/dist/dht/registry.js.map +1 -0
- package/dist/discovery.d.ts +43 -0
- package/dist/discovery.js +70 -0
- package/dist/discovery.js.map +1 -0
- package/dist/identity/index.d.ts +34 -0
- package/dist/identity/index.js +46 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/keys.d.ts +26 -0
- package/dist/identity/keys.js +49 -0
- package/dist/identity/keys.js.map +1 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +299 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +32 -0
- package/dist/logger.js.map +1 -0
- package/dist/node.d.ts +47 -0
- package/dist/node.js +136 -0
- package/dist/node.js.map +1 -0
- package/dist/protocol/index.d.ts +11 -0
- package/dist/protocol/index.js +66 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/types.d.ts +197 -0
- package/dist/protocol/types.js +18 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/signaling/onboarding.d.ts +10 -0
- package/dist/signaling/onboarding.js +40 -0
- package/dist/signaling/onboarding.js.map +1 -0
- package/dist/signaling/server.d.ts +35 -0
- package/dist/signaling/server.js +140 -0
- package/dist/signaling/server.js.map +1 -0
- package/dist/storage/index.d.ts +31 -0
- package/dist/storage/index.js +103 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/mirror.d.ts +9 -0
- package/dist/storage/mirror.js +24 -0
- package/dist/storage/mirror.js.map +1 -0
- package/dist/storage/pin.d.ts +8 -0
- package/dist/storage/pin.js +42 -0
- package/dist/storage/pin.js.map +1 -0
- package/dist/storage/propagation.d.ts +32 -0
- package/dist/storage/propagation.js +89 -0
- package/dist/storage/propagation.js.map +1 -0
- package/package.json +122 -0
package/dist/node.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* libp2p node creation and configuration
|
|
3
|
+
*
|
|
4
|
+
* Responsible for creating and configuring the libp2p node with:
|
|
5
|
+
* - Transport: TCP, WebSockets, Circuit Relay v2
|
|
6
|
+
* - Encryption: Noise
|
|
7
|
+
* - Muxer: Yamux
|
|
8
|
+
* - Discovery: mDNS, Bootstrap (when peers provided), Pubsub Peer Discovery
|
|
9
|
+
* - Services: Identify, KadDHT (/nexus/kad/1.0.0), GossipSub, Circuit Relay Server
|
|
10
|
+
*
|
|
11
|
+
* Discovery cascade (in priority order):
|
|
12
|
+
* 1. Signaling Server peers — callers must pass these in via signalingPeers
|
|
13
|
+
* 2. Public bootstrap WSS nodes — enabled by discoveryConfig.usePublicBootstrap
|
|
14
|
+
* 3. Pubsub peer discovery — all agents subscribe to NEXUS_DISCOVERY_TOPIC
|
|
15
|
+
* 4. mDNS — local-network zero-config discovery
|
|
16
|
+
* 5. Circuit Relay v2 — NAT traversal (enabled for non-light roles)
|
|
17
|
+
*/
|
|
18
|
+
import { createLibp2p } from 'libp2p';
|
|
19
|
+
import { noise } from '@chainsafe/libp2p-noise';
|
|
20
|
+
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
21
|
+
import { tcp } from '@libp2p/tcp';
|
|
22
|
+
import { webSockets } from '@libp2p/websockets';
|
|
23
|
+
import { kadDHT } from '@libp2p/kad-dht';
|
|
24
|
+
import { gossipsub } from '@libp2p/gossipsub';
|
|
25
|
+
import { identify } from '@libp2p/identify';
|
|
26
|
+
import { bootstrap } from '@libp2p/bootstrap';
|
|
27
|
+
import { mdns } from '@libp2p/mdns';
|
|
28
|
+
import { ping } from '@libp2p/ping';
|
|
29
|
+
import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2';
|
|
30
|
+
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery';
|
|
31
|
+
import { msgIdFn as nexusMsgIdFn, PROTOCOLS } from './protocol/index.js';
|
|
32
|
+
import { resolveDiscovery, buildBootstrapList, NEXUS_DISCOVERY_TOPIC, } from './discovery.js';
|
|
33
|
+
import { createLogger } from './logger.js';
|
|
34
|
+
const log = createLogger('node');
|
|
35
|
+
/**
|
|
36
|
+
* Create and start a fully-configured libp2p nexus node.
|
|
37
|
+
*
|
|
38
|
+
* The node uses:
|
|
39
|
+
* - TCP + WebSockets + Circuit Relay v2 transports
|
|
40
|
+
* - Noise connection encryption
|
|
41
|
+
* - Yamux stream multiplexing
|
|
42
|
+
* - Identify service for protocol/address exchange
|
|
43
|
+
* - KadDHT on the /nexus/kad/1.0.0 protocol (client mode for 'light' role)
|
|
44
|
+
* - GossipSub for pub/sub messaging
|
|
45
|
+
* - Pubsub peer discovery on the global nexus discovery topic
|
|
46
|
+
* - mDNS for local-network peer discovery (controllable via discoveryConfig)
|
|
47
|
+
* - Bootstrap peer discovery (when the resolved bootstrap list is non-empty)
|
|
48
|
+
* - Circuit relay server for non-light roles (helps NAT'd peers connect)
|
|
49
|
+
*
|
|
50
|
+
* @param config - Nexus node configuration
|
|
51
|
+
* @param privateKey - Ed25519 private key for this node's identity
|
|
52
|
+
* @param discoveryConfig - Optional discovery cascade settings (defaults applied)
|
|
53
|
+
* @param signalingPeers - Bootstrap peers fetched from the signaling server (Level 1)
|
|
54
|
+
*/
|
|
55
|
+
export async function createNexusNode(config, privateKey, discoveryConfig, signalingPeers = []) {
|
|
56
|
+
// Resolve discovery settings, merging config-level fields and explicit param
|
|
57
|
+
const discovery = resolveDiscovery({
|
|
58
|
+
usePublicBootstrap: config.usePublicBootstrap,
|
|
59
|
+
enableCircuitRelay: config.enableCircuitRelay,
|
|
60
|
+
enablePubsubDiscovery: config.enablePubsubDiscovery,
|
|
61
|
+
enableMdns: config.enableMdns,
|
|
62
|
+
...discoveryConfig,
|
|
63
|
+
});
|
|
64
|
+
// Build the combined bootstrap peer list from all sources
|
|
65
|
+
// Merge config.bootstrapPeers (operator-supplied) into signalingPeers position
|
|
66
|
+
const allSignalingPeers = [...signalingPeers, ...config.bootstrapPeers];
|
|
67
|
+
const bootstrapList = buildBootstrapList(discovery, allSignalingPeers);
|
|
68
|
+
// Build peer discovery modules
|
|
69
|
+
const peerDiscovery = [];
|
|
70
|
+
if (bootstrapList.length > 0) {
|
|
71
|
+
peerDiscovery.push(bootstrap({ list: bootstrapList }));
|
|
72
|
+
}
|
|
73
|
+
if (discovery.enableMdns) {
|
|
74
|
+
peerDiscovery.push(mdns());
|
|
75
|
+
}
|
|
76
|
+
if (discovery.enablePubsubDiscovery) {
|
|
77
|
+
peerDiscovery.push(pubsubPeerDiscovery({
|
|
78
|
+
interval: 10_000, // Re-announce every 10 seconds
|
|
79
|
+
topics: [NEXUS_DISCOVERY_TOPIC],
|
|
80
|
+
listenOnly: false,
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
// Build transport list — always include circuit relay transport so we can
|
|
84
|
+
// both dial and receive relay-mediated connections.
|
|
85
|
+
const transports = [
|
|
86
|
+
tcp(),
|
|
87
|
+
webSockets(),
|
|
88
|
+
...(discovery.enableCircuitRelay ? [circuitRelayTransport()] : []),
|
|
89
|
+
];
|
|
90
|
+
// Listen addresses — add /p2p-circuit so relay-connected peers can reach us
|
|
91
|
+
const listenAddresses = [
|
|
92
|
+
...config.listenAddresses,
|
|
93
|
+
...(discovery.enableCircuitRelay ? ['/p2p-circuit'] : []),
|
|
94
|
+
];
|
|
95
|
+
// Adapt our protocol-level msgIdFn to the gossipsub MsgIdFn signature.
|
|
96
|
+
// Gossipsub guarantees msg.data is always a Uint8Array for received messages.
|
|
97
|
+
function gossipMsgIdFn(msg) {
|
|
98
|
+
return nexusMsgIdFn({ data: msg.data });
|
|
99
|
+
}
|
|
100
|
+
// Build services — full/storage nodes act as circuit relay servers, helping
|
|
101
|
+
// NAT'd light nodes connect to the rest of the network.
|
|
102
|
+
const isRelayServer = discovery.enableCircuitRelay && config.role !== 'light';
|
|
103
|
+
const node = await createLibp2p({
|
|
104
|
+
privateKey,
|
|
105
|
+
addresses: {
|
|
106
|
+
listen: listenAddresses,
|
|
107
|
+
},
|
|
108
|
+
transports,
|
|
109
|
+
connectionEncrypters: [noise()],
|
|
110
|
+
streamMuxers: [yamux()],
|
|
111
|
+
peerDiscovery,
|
|
112
|
+
services: {
|
|
113
|
+
identify: identify(),
|
|
114
|
+
ping: ping(),
|
|
115
|
+
dht: kadDHT({
|
|
116
|
+
protocol: PROTOCOLS.DHT,
|
|
117
|
+
clientMode: config.role === 'light',
|
|
118
|
+
}),
|
|
119
|
+
pubsub: gossipsub({
|
|
120
|
+
emitSelf: false,
|
|
121
|
+
allowPublishToZeroTopicPeers: true,
|
|
122
|
+
msgIdFn: gossipMsgIdFn,
|
|
123
|
+
}),
|
|
124
|
+
...(isRelayServer ? { relay: circuitRelayServer() } : {}),
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
log.info(`Node created with PeerId: ${node.peerId.toString()}`);
|
|
128
|
+
log.info(`Role: ${config.role}`);
|
|
129
|
+
log.info(`Discovery: bootstrap=${bootstrapList.length} peers, ` +
|
|
130
|
+
`mdns=${discovery.enableMdns}, ` +
|
|
131
|
+
`pubsub=${discovery.enablePubsubDiscovery}, ` +
|
|
132
|
+
`relay=${discovery.enableCircuitRelay}`);
|
|
133
|
+
log.info(`Listening on: ${node.getMultiaddrs().map((a) => a.toString()).join(', ')}`);
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type MessageType, type NexusMessage } from './types.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export declare function uuidv7(): string;
|
|
4
|
+
export declare function createMessage(type: MessageType, topic: string, sender: string, payload: NexusMessage['payload'], references?: string[]): NexusMessage;
|
|
5
|
+
export declare function encodeMessage(msg: NexusMessage): Uint8Array;
|
|
6
|
+
export declare function decodeMessage(data: Uint8Array): NexusMessage;
|
|
7
|
+
export declare function msgIdFn(msg: {
|
|
8
|
+
data: Uint8Array | null;
|
|
9
|
+
}): Uint8Array;
|
|
10
|
+
export declare function roomTopic(roomId: string): string;
|
|
11
|
+
export declare function ephemeralTopic(sessionId: string): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION, TOPICS, } from './types.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
// UUIDv7 generation (no external dependency)
|
|
4
|
+
// UUIDv7 = unix_ms (48 bits) + version (4 bits) + rand_a (12 bits) + variant (2 bits) + rand_b (62 bits)
|
|
5
|
+
export function uuidv7() {
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
const bytes = new Uint8Array(16);
|
|
8
|
+
// timestamp (48 bits, big-endian)
|
|
9
|
+
bytes[0] = (now / 2 ** 40) & 0xff;
|
|
10
|
+
bytes[1] = (now / 2 ** 32) & 0xff;
|
|
11
|
+
bytes[2] = (now / 2 ** 24) & 0xff;
|
|
12
|
+
bytes[3] = (now / 2 ** 16) & 0xff;
|
|
13
|
+
bytes[4] = (now / 2 ** 8) & 0xff;
|
|
14
|
+
bytes[5] = now & 0xff;
|
|
15
|
+
// random (80 bits)
|
|
16
|
+
crypto.getRandomValues(bytes.subarray(6));
|
|
17
|
+
// version 7
|
|
18
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x70;
|
|
19
|
+
// variant 10
|
|
20
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
21
|
+
const hex = Array.from(bytes)
|
|
22
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
23
|
+
.join('');
|
|
24
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
25
|
+
}
|
|
26
|
+
// Create a NexusMessage
|
|
27
|
+
export function createMessage(type, topic, sender, payload, references = []) {
|
|
28
|
+
return {
|
|
29
|
+
version: PROTOCOL_VERSION,
|
|
30
|
+
type,
|
|
31
|
+
id: uuidv7(),
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
sender,
|
|
34
|
+
topic,
|
|
35
|
+
payload,
|
|
36
|
+
references,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Serialize/deserialize messages
|
|
40
|
+
export function encodeMessage(msg) {
|
|
41
|
+
return new TextEncoder().encode(JSON.stringify(msg));
|
|
42
|
+
}
|
|
43
|
+
export function decodeMessage(data) {
|
|
44
|
+
return JSON.parse(new TextDecoder().decode(data));
|
|
45
|
+
}
|
|
46
|
+
// Message ID function for GossipSub deduplication
|
|
47
|
+
export function msgIdFn(msg) {
|
|
48
|
+
if (!msg.data)
|
|
49
|
+
return new Uint8Array(0);
|
|
50
|
+
try {
|
|
51
|
+
const envelope = JSON.parse(new TextDecoder().decode(msg.data));
|
|
52
|
+
return new TextEncoder().encode(envelope.id);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// fallback: use raw data hash (we'll just use the first 32 bytes as a simple fingerprint)
|
|
56
|
+
return msg.data.subarray(0, 32);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Topic helpers
|
|
60
|
+
export function roomTopic(roomId) {
|
|
61
|
+
return `${TOPICS.ROOM_PREFIX}${roomId}`;
|
|
62
|
+
}
|
|
63
|
+
export function ephemeralTopic(sessionId) {
|
|
64
|
+
return `${TOPICS.EPHEMERAL_PREFIX}${sessionId}`;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/protocol/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export declare const PROTOCOL_VERSION: 1;
|
|
2
|
+
export declare const PROTOCOLS: {
|
|
3
|
+
readonly DHT: "/nexus/kad/1.0.0";
|
|
4
|
+
readonly SYNC: "/nexus/sync/1.0.0";
|
|
5
|
+
readonly HANDSHAKE: "/nexus/handshake/1.0.0";
|
|
6
|
+
readonly DM: "/nexus/dm/1.0.0";
|
|
7
|
+
readonly CAPABILITY_INVOKE: "/nexus/capability/invoke/1.0.0";
|
|
8
|
+
};
|
|
9
|
+
export declare const TOPICS: {
|
|
10
|
+
readonly META: "/nexus/meta";
|
|
11
|
+
readonly ROOM_PREFIX: "/nexus/room/";
|
|
12
|
+
readonly EPHEMERAL_PREFIX: "/nexus/ephemeral/";
|
|
13
|
+
readonly CAPABILITY_PREFIX: "/nexus/capability/";
|
|
14
|
+
};
|
|
15
|
+
export type MessageType = 'chat' | 'presence' | 'meta' | 'capability' | 'sync';
|
|
16
|
+
export type PresenceStatus = 'online' | 'idle' | 'busy' | 'offline';
|
|
17
|
+
export type AgentRole = 'light' | 'full' | 'storage';
|
|
18
|
+
export type RoomType = 'persistent' | 'ephemeral';
|
|
19
|
+
export type RoomAccess = 'public';
|
|
20
|
+
export type ContentFormat = 'text/plain' | 'text/markdown' | 'application/json';
|
|
21
|
+
export interface NexusMessage {
|
|
22
|
+
version: typeof PROTOCOL_VERSION;
|
|
23
|
+
type: MessageType;
|
|
24
|
+
id: string;
|
|
25
|
+
timestamp: number;
|
|
26
|
+
sender: string;
|
|
27
|
+
topic: string;
|
|
28
|
+
payload: ChatPayload | PresencePayload | MetaPayload | CapabilityPayload | SyncPayload;
|
|
29
|
+
references: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface ChatPayload {
|
|
32
|
+
content: string;
|
|
33
|
+
format: ContentFormat;
|
|
34
|
+
replyTo: string | null;
|
|
35
|
+
threadId: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface PresencePayload {
|
|
38
|
+
status: PresenceStatus;
|
|
39
|
+
capabilities: string[];
|
|
40
|
+
agentName: string;
|
|
41
|
+
agentType: string;
|
|
42
|
+
version: string;
|
|
43
|
+
}
|
|
44
|
+
export interface MetaPayload {
|
|
45
|
+
action: string;
|
|
46
|
+
roomId?: string;
|
|
47
|
+
roomManifest?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
export interface CapabilityPayload {
|
|
51
|
+
capabilities: CapabilityDefinition[];
|
|
52
|
+
}
|
|
53
|
+
export interface CapabilityDefinition {
|
|
54
|
+
name: string;
|
|
55
|
+
protocol: string;
|
|
56
|
+
description: string;
|
|
57
|
+
pricing: string;
|
|
58
|
+
rateLimit: string;
|
|
59
|
+
}
|
|
60
|
+
export interface SyncPayload {
|
|
61
|
+
action: 'request' | 'response';
|
|
62
|
+
since?: number;
|
|
63
|
+
limit?: number;
|
|
64
|
+
historyRoot?: string;
|
|
65
|
+
messageCount?: number;
|
|
66
|
+
oldestTimestamp?: number;
|
|
67
|
+
newestTimestamp?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface AgentProfile {
|
|
70
|
+
schema: 'nexus:agent-profile:v1';
|
|
71
|
+
peerId: string;
|
|
72
|
+
name: string;
|
|
73
|
+
description: string;
|
|
74
|
+
type: string;
|
|
75
|
+
capabilities: CapabilityDefinition[];
|
|
76
|
+
role: AgentRole;
|
|
77
|
+
transports: string[];
|
|
78
|
+
createdAt: number;
|
|
79
|
+
updatedAt: number;
|
|
80
|
+
previousVersion: string | null;
|
|
81
|
+
}
|
|
82
|
+
export interface RoomManifest {
|
|
83
|
+
schema: 'nexus:room-manifest:v1';
|
|
84
|
+
roomId: string;
|
|
85
|
+
topic: string;
|
|
86
|
+
name: string;
|
|
87
|
+
description: string;
|
|
88
|
+
createdBy: string;
|
|
89
|
+
createdAt: number;
|
|
90
|
+
type: RoomType;
|
|
91
|
+
access: RoomAccess;
|
|
92
|
+
retention: {
|
|
93
|
+
policy: 'community-pinned';
|
|
94
|
+
minPinners: number;
|
|
95
|
+
archiveAfterMs: number;
|
|
96
|
+
};
|
|
97
|
+
historyRoot: string | null;
|
|
98
|
+
memberCount: number;
|
|
99
|
+
previousVersion: string | null;
|
|
100
|
+
}
|
|
101
|
+
export interface MessagePage {
|
|
102
|
+
schema: 'nexus:message-page:v1';
|
|
103
|
+
roomId: string;
|
|
104
|
+
pageIndex: number;
|
|
105
|
+
count: number;
|
|
106
|
+
timestamp: {
|
|
107
|
+
first: number;
|
|
108
|
+
last: number;
|
|
109
|
+
};
|
|
110
|
+
messages: StoredMessage[];
|
|
111
|
+
prev: {
|
|
112
|
+
'/': string;
|
|
113
|
+
} | null;
|
|
114
|
+
}
|
|
115
|
+
export interface StoredMessage {
|
|
116
|
+
id: string;
|
|
117
|
+
timestamp: number;
|
|
118
|
+
sender: string;
|
|
119
|
+
payload: ChatPayload;
|
|
120
|
+
}
|
|
121
|
+
export interface HandshakeInit {
|
|
122
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
123
|
+
agentName: string;
|
|
124
|
+
agentType: string;
|
|
125
|
+
capabilities: string[];
|
|
126
|
+
rooms: string[];
|
|
127
|
+
role: AgentRole;
|
|
128
|
+
clientVersion: string;
|
|
129
|
+
}
|
|
130
|
+
export interface HandshakeAck {
|
|
131
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
132
|
+
agentName: string;
|
|
133
|
+
agentType: string;
|
|
134
|
+
capabilities: string[];
|
|
135
|
+
rooms: string[];
|
|
136
|
+
role: AgentRole;
|
|
137
|
+
clientVersion: string;
|
|
138
|
+
}
|
|
139
|
+
export interface InvocationRequest {
|
|
140
|
+
requestId: string;
|
|
141
|
+
capability: string;
|
|
142
|
+
input: unknown;
|
|
143
|
+
maxWaitMs: number;
|
|
144
|
+
}
|
|
145
|
+
export interface InvocationResponse {
|
|
146
|
+
requestId: string;
|
|
147
|
+
status: 'success' | 'error';
|
|
148
|
+
output?: unknown;
|
|
149
|
+
error?: string;
|
|
150
|
+
processingMs: number;
|
|
151
|
+
}
|
|
152
|
+
export interface BootstrapResponse {
|
|
153
|
+
peers: string[];
|
|
154
|
+
network: {
|
|
155
|
+
peerCount: number;
|
|
156
|
+
roomCount: number;
|
|
157
|
+
protocolVersion: number;
|
|
158
|
+
minClientVersion: string;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export interface NetworkResponse {
|
|
162
|
+
peerCount: number;
|
|
163
|
+
roomCount: number;
|
|
164
|
+
messageRate: number;
|
|
165
|
+
storageProviders: number;
|
|
166
|
+
protocolVersion: number;
|
|
167
|
+
uptime: number;
|
|
168
|
+
rooms: RoomInfo[];
|
|
169
|
+
}
|
|
170
|
+
export interface RoomInfo {
|
|
171
|
+
roomId: string;
|
|
172
|
+
name: string;
|
|
173
|
+
topic: string;
|
|
174
|
+
memberCount: number;
|
|
175
|
+
type: RoomType;
|
|
176
|
+
access: RoomAccess;
|
|
177
|
+
manifest: string;
|
|
178
|
+
}
|
|
179
|
+
export interface ContributeOptions {
|
|
180
|
+
storage?: boolean;
|
|
181
|
+
relay?: boolean;
|
|
182
|
+
mirror?: string[];
|
|
183
|
+
}
|
|
184
|
+
export interface NexusEvents {
|
|
185
|
+
'peer:discovered': (profile: AgentProfile) => void;
|
|
186
|
+
'peer:connected': (peerId: string) => void;
|
|
187
|
+
'peer:disconnected': (peerId: string) => void;
|
|
188
|
+
'error': (error: Error) => void;
|
|
189
|
+
}
|
|
190
|
+
export interface RoomEvents {
|
|
191
|
+
'message': (msg: NexusMessage) => void;
|
|
192
|
+
'presence': (presence: NexusMessage) => void;
|
|
193
|
+
'sync': (progress: {
|
|
194
|
+
loaded: number;
|
|
195
|
+
total: number;
|
|
196
|
+
}) => void;
|
|
197
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Protocol version
|
|
2
|
+
export const PROTOCOL_VERSION = 1;
|
|
3
|
+
// Custom protocol identifiers
|
|
4
|
+
export const PROTOCOLS = {
|
|
5
|
+
DHT: '/nexus/kad/1.0.0',
|
|
6
|
+
SYNC: '/nexus/sync/1.0.0',
|
|
7
|
+
HANDSHAKE: '/nexus/handshake/1.0.0',
|
|
8
|
+
DM: '/nexus/dm/1.0.0',
|
|
9
|
+
CAPABILITY_INVOKE: '/nexus/capability/invoke/1.0.0',
|
|
10
|
+
};
|
|
11
|
+
// GossipSub topic prefixes
|
|
12
|
+
export const TOPICS = {
|
|
13
|
+
META: '/nexus/meta',
|
|
14
|
+
ROOM_PREFIX: '/nexus/room/',
|
|
15
|
+
EPHEMERAL_PREFIX: '/nexus/ephemeral/',
|
|
16
|
+
CAPABILITY_PREFIX: '/nexus/capability/',
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/protocol/types.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAU,CAAC;AAE3C,8BAA8B;AAC9B,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,GAAG,EAAE,kBAAkB;IACvB,IAAI,EAAE,mBAAmB;IACzB,SAAS,EAAE,wBAAwB;IACnC,EAAE,EAAE,iBAAiB;IACrB,iBAAiB,EAAE,gCAAgC;CAC3C,CAAC;AAEX,2BAA2B;AAC3B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,cAAc;IAC3B,gBAAgB,EAAE,mBAAmB;IACrC,iBAAiB,EAAE,oBAAoB;CAC/B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent onboarding protocol
|
|
3
|
+
*
|
|
4
|
+
* Manages the process of new agents joining the network:
|
|
5
|
+
* - Identity verification
|
|
6
|
+
* - Capability announcement
|
|
7
|
+
* - Peer discovery and initial connections
|
|
8
|
+
*/
|
|
9
|
+
import type { BootstrapResponse } from '../protocol/types.js';
|
|
10
|
+
export declare function fetchBootstrapPeers(signalingServer: string): Promise<BootstrapResponse>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent onboarding protocol
|
|
3
|
+
*
|
|
4
|
+
* Manages the process of new agents joining the network:
|
|
5
|
+
* - Identity verification
|
|
6
|
+
* - Capability announcement
|
|
7
|
+
* - Peer discovery and initial connections
|
|
8
|
+
*/
|
|
9
|
+
import { createLogger } from '../logger.js';
|
|
10
|
+
const log = createLogger('onboarding');
|
|
11
|
+
// Client-side onboarding: fetch bootstrap info from signaling server
|
|
12
|
+
export async function fetchBootstrapPeers(signalingServer) {
|
|
13
|
+
const url = `${signalingServer}/api/v1/bootstrap`;
|
|
14
|
+
log.info(`Fetching bootstrap peers from ${url}`);
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
signal: AbortSignal.timeout(10_000),
|
|
18
|
+
});
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(`Bootstrap request failed: ${response.status} ${response.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
log.info(`Received ${data.peers.length} bootstrap peers`);
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
log.warn(`Failed to fetch bootstrap peers: ${err}`);
|
|
28
|
+
// Return empty response — network can still function via mDNS or cached peers
|
|
29
|
+
return {
|
|
30
|
+
peers: [],
|
|
31
|
+
network: {
|
|
32
|
+
peerCount: 0,
|
|
33
|
+
roomCount: 0,
|
|
34
|
+
protocolVersion: 1,
|
|
35
|
+
minClientVersion: '0.1.0',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/signaling/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAEvC,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,eAAuB;IAC/D,MAAM,GAAG,GAAG,GAAG,eAAe,mBAAmB,CAAC;IAClD,GAAG,CAAC,IAAI,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;SACpC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAuB,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,MAAM,kBAAkB,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,oCAAoC,GAAG,EAAE,CAAC,CAAC;QACpD,8EAA8E;QAC9E,OAAO;YACL,KAAK,EAAE,EAAE;YACT,OAAO,EAAE;gBACP,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,CAAC;gBACZ,eAAe,EAAE,CAAC;gBAClB,gBAAgB,EAAE,OAAO;aAC1B;SACF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP/WebSocket signaling server
|
|
3
|
+
*
|
|
4
|
+
* Provides a signaling server for WebRTC peer connection establishment.
|
|
5
|
+
* Handles SDP offer/answer exchange and ICE candidate relay between agents.
|
|
6
|
+
*/
|
|
7
|
+
import type { RoomInfo } from '../protocol/types.js';
|
|
8
|
+
export interface SignalingServerOptions {
|
|
9
|
+
port: number;
|
|
10
|
+
host?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class SignalingServer {
|
|
13
|
+
private server;
|
|
14
|
+
private port;
|
|
15
|
+
private host;
|
|
16
|
+
private bootstrapPeers;
|
|
17
|
+
private knownRooms;
|
|
18
|
+
private peerCount;
|
|
19
|
+
private startTime;
|
|
20
|
+
constructor(options: SignalingServerOptions);
|
|
21
|
+
start(): Promise<void>;
|
|
22
|
+
stop(): Promise<void>;
|
|
23
|
+
updateState(state: {
|
|
24
|
+
bootstrapPeers?: string[];
|
|
25
|
+
knownRooms?: RoomInfo[];
|
|
26
|
+
peerCount?: number;
|
|
27
|
+
}): void;
|
|
28
|
+
private handleRequest;
|
|
29
|
+
private handleBootstrap;
|
|
30
|
+
private handleNetwork;
|
|
31
|
+
private handleRooms;
|
|
32
|
+
private handleLanding;
|
|
33
|
+
private sendJSON;
|
|
34
|
+
private send404;
|
|
35
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP/WebSocket signaling server
|
|
3
|
+
*
|
|
4
|
+
* Provides a signaling server for WebRTC peer connection establishment.
|
|
5
|
+
* Handles SDP offer/answer exchange and ICE candidate relay between agents.
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from 'node:http';
|
|
8
|
+
import { createLogger } from '../logger.js';
|
|
9
|
+
const log = createLogger('signaling');
|
|
10
|
+
export class SignalingServer {
|
|
11
|
+
server = null;
|
|
12
|
+
port;
|
|
13
|
+
host;
|
|
14
|
+
// Network state (populated by the nexus node)
|
|
15
|
+
bootstrapPeers = [];
|
|
16
|
+
knownRooms = [];
|
|
17
|
+
peerCount = 0;
|
|
18
|
+
startTime = Date.now();
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.port = options.port;
|
|
21
|
+
this.host = options.host ?? '0.0.0.0';
|
|
22
|
+
}
|
|
23
|
+
async start() {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
26
|
+
this.server.on('error', reject);
|
|
27
|
+
this.server.listen(this.port, this.host, () => {
|
|
28
|
+
log.info(`Signaling server listening on ${this.host}:${this.port}`);
|
|
29
|
+
resolve();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async stop() {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
if (this.server) {
|
|
36
|
+
this.server.close(() => {
|
|
37
|
+
log.info('Signaling server stopped');
|
|
38
|
+
resolve();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
resolve();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Update network state (called by the nexus node periodically)
|
|
47
|
+
updateState(state) {
|
|
48
|
+
if (state.bootstrapPeers)
|
|
49
|
+
this.bootstrapPeers = state.bootstrapPeers;
|
|
50
|
+
if (state.knownRooms)
|
|
51
|
+
this.knownRooms = state.knownRooms;
|
|
52
|
+
if (state.peerCount !== undefined)
|
|
53
|
+
this.peerCount = state.peerCount;
|
|
54
|
+
}
|
|
55
|
+
handleRequest(req, res) {
|
|
56
|
+
// CORS headers
|
|
57
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
58
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
59
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
60
|
+
if (req.method === 'OPTIONS') {
|
|
61
|
+
res.writeHead(204);
|
|
62
|
+
res.end();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
66
|
+
switch (url.pathname) {
|
|
67
|
+
case '/api/v1/bootstrap':
|
|
68
|
+
this.handleBootstrap(res);
|
|
69
|
+
break;
|
|
70
|
+
case '/api/v1/network':
|
|
71
|
+
this.handleNetwork(res);
|
|
72
|
+
break;
|
|
73
|
+
case '/api/v1/rooms':
|
|
74
|
+
this.handleRooms(res);
|
|
75
|
+
break;
|
|
76
|
+
case '/':
|
|
77
|
+
this.handleLanding(res);
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
this.send404(res);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
handleBootstrap(res) {
|
|
85
|
+
const response = {
|
|
86
|
+
peers: this.bootstrapPeers.slice(0, 20), // max 20 peers
|
|
87
|
+
network: {
|
|
88
|
+
peerCount: this.peerCount,
|
|
89
|
+
roomCount: this.knownRooms.length,
|
|
90
|
+
protocolVersion: 1,
|
|
91
|
+
minClientVersion: '0.1.0',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
this.sendJSON(res, response);
|
|
95
|
+
}
|
|
96
|
+
handleNetwork(res) {
|
|
97
|
+
const response = {
|
|
98
|
+
peerCount: this.peerCount,
|
|
99
|
+
roomCount: this.knownRooms.length,
|
|
100
|
+
messageRate: 0,
|
|
101
|
+
storageProviders: 0,
|
|
102
|
+
protocolVersion: 1,
|
|
103
|
+
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
|
104
|
+
rooms: this.knownRooms,
|
|
105
|
+
};
|
|
106
|
+
this.sendJSON(res, response);
|
|
107
|
+
}
|
|
108
|
+
handleRooms(res) {
|
|
109
|
+
this.sendJSON(res, { rooms: this.knownRooms });
|
|
110
|
+
}
|
|
111
|
+
handleLanding(res) {
|
|
112
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
113
|
+
res.end([
|
|
114
|
+
'OpenAgents Nexus',
|
|
115
|
+
'================',
|
|
116
|
+
'',
|
|
117
|
+
'Decentralized agent communication platform.',
|
|
118
|
+
'No central authority. No data collection. No surveillance.',
|
|
119
|
+
'',
|
|
120
|
+
'API Endpoints:',
|
|
121
|
+
' GET /api/v1/bootstrap - Get bootstrap peers',
|
|
122
|
+
' GET /api/v1/network - Network statistics',
|
|
123
|
+
' GET /api/v1/rooms - Available rooms',
|
|
124
|
+
'',
|
|
125
|
+
'Get started:',
|
|
126
|
+
' npm install @openagents/nexus-client',
|
|
127
|
+
'',
|
|
128
|
+
'Source: https://github.com/openagents/nexus',
|
|
129
|
+
].join('\n'));
|
|
130
|
+
}
|
|
131
|
+
sendJSON(res, data) {
|
|
132
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
133
|
+
res.end(JSON.stringify(data));
|
|
134
|
+
}
|
|
135
|
+
send404(res) {
|
|
136
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
137
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=server.js.map
|