@ruvector/edge-net 0.4.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/firebase-signaling.js +77 -49
- package/p2p.js +1 -1
- package/package.json +2 -9
package/firebase-signaling.js
CHANGED
|
@@ -95,13 +95,14 @@ export async function getFirebaseConfigAsync() {
|
|
|
95
95
|
export const DEFAULT_FIREBASE_CONFIG = null;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Signaling
|
|
98
|
+
* Signaling collection names in Firestore
|
|
99
|
+
* Each is a top-level collection (Firestore requires collection/document pairs)
|
|
99
100
|
*/
|
|
100
101
|
export const SIGNALING_PATHS = {
|
|
101
|
-
peers: '
|
|
102
|
-
signals: '
|
|
103
|
-
rooms: '
|
|
104
|
-
ledger: '
|
|
102
|
+
peers: 'edgenet_peers',
|
|
103
|
+
signals: 'edgenet_signals',
|
|
104
|
+
rooms: 'edgenet_rooms',
|
|
105
|
+
ledger: 'edgenet_ledger',
|
|
105
106
|
};
|
|
106
107
|
|
|
107
108
|
// ============================================
|
|
@@ -167,26 +168,21 @@ export class FirebaseSignaling extends EventEmitter {
|
|
|
167
168
|
try {
|
|
168
169
|
// Dynamic import Firebase (tree-shakeable)
|
|
169
170
|
const { initializeApp, getApps } = await import('firebase/app');
|
|
170
|
-
const { getFirestore, collection, doc, setDoc, onSnapshot, deleteDoc, query, where, orderBy, limit } = await import('firebase/firestore');
|
|
171
|
-
const { getDatabase, ref, set, onValue, onDisconnect, serverTimestamp } = await import('firebase/database');
|
|
171
|
+
const { getFirestore, collection, doc, setDoc, onSnapshot, deleteDoc, query, where, orderBy, limit, serverTimestamp } = await import('firebase/firestore');
|
|
172
172
|
|
|
173
|
-
// Store Firebase methods for later use
|
|
173
|
+
// Store Firebase methods for later use (Firestore only - no RTDB)
|
|
174
174
|
this.firebase = {
|
|
175
|
-
collection, doc, setDoc, onSnapshot, deleteDoc, query, where, orderBy, limit,
|
|
176
|
-
ref, set, onValue, onDisconnect, serverTimestamp
|
|
175
|
+
collection, doc, setDoc, onSnapshot, deleteDoc, query, where, orderBy, limit, serverTimestamp
|
|
177
176
|
};
|
|
178
177
|
|
|
179
178
|
// Initialize or reuse existing app
|
|
180
179
|
const apps = getApps();
|
|
181
180
|
this.app = apps.length ? apps[0] : initializeApp(this.config);
|
|
182
181
|
|
|
183
|
-
// Initialize Firestore (for signaling)
|
|
182
|
+
// Initialize Firestore (for signaling AND presence - no RTDB needed)
|
|
184
183
|
this.db = getFirestore(this.app);
|
|
185
184
|
|
|
186
|
-
//
|
|
187
|
-
this.rtdb = getDatabase(this.app);
|
|
188
|
-
|
|
189
|
-
// Register presence
|
|
185
|
+
// Register presence in Firestore
|
|
190
186
|
await this.registerPresence();
|
|
191
187
|
|
|
192
188
|
// Listen for peers
|
|
@@ -196,7 +192,7 @@ export class FirebaseSignaling extends EventEmitter {
|
|
|
196
192
|
this.subscribeToSignals();
|
|
197
193
|
|
|
198
194
|
this.isConnected = true;
|
|
199
|
-
console.log(' ✅ Firebase signaling connected');
|
|
195
|
+
console.log(' ✅ Firebase signaling connected (Firestore)');
|
|
200
196
|
|
|
201
197
|
this.emit('connected');
|
|
202
198
|
return true;
|
|
@@ -209,54 +205,76 @@ export class FirebaseSignaling extends EventEmitter {
|
|
|
209
205
|
}
|
|
210
206
|
|
|
211
207
|
/**
|
|
212
|
-
* Register this peer's presence in
|
|
208
|
+
* Register this peer's presence in Firestore
|
|
213
209
|
*/
|
|
214
210
|
async registerPresence() {
|
|
215
|
-
const {
|
|
211
|
+
const { doc, setDoc, serverTimestamp } = this.firebase;
|
|
216
212
|
|
|
217
|
-
const presenceRef =
|
|
213
|
+
const presenceRef = doc(this.db, SIGNALING_PATHS.peers, this.peerId);
|
|
218
214
|
|
|
219
|
-
// Set online status
|
|
220
|
-
await
|
|
215
|
+
// Set online status in Firestore
|
|
216
|
+
await setDoc(presenceRef, {
|
|
221
217
|
peerId: this.peerId,
|
|
222
218
|
room: this.room,
|
|
223
219
|
online: true,
|
|
224
220
|
lastSeen: serverTimestamp(),
|
|
225
221
|
capabilities: ['compute', 'storage', 'verify'],
|
|
226
|
-
});
|
|
222
|
+
}, { merge: true });
|
|
227
223
|
|
|
228
|
-
//
|
|
229
|
-
|
|
224
|
+
// Set up heartbeat to maintain presence (Firestore doesn't have onDisconnect)
|
|
225
|
+
this._heartbeatInterval = setInterval(async () => {
|
|
226
|
+
try {
|
|
227
|
+
await setDoc(presenceRef, { lastSeen: serverTimestamp() }, { merge: true });
|
|
228
|
+
} catch (e) {
|
|
229
|
+
// Ignore heartbeat errors
|
|
230
|
+
}
|
|
231
|
+
}, 30000);
|
|
230
232
|
|
|
231
233
|
console.log(` 📡 Registered presence: ${this.peerId.slice(0, 8)}...`);
|
|
232
234
|
}
|
|
233
235
|
|
|
234
236
|
/**
|
|
235
|
-
* Subscribe to peer presence updates
|
|
237
|
+
* Subscribe to peer presence updates (using Firestore)
|
|
236
238
|
*/
|
|
237
239
|
subscribeToPeers() {
|
|
238
|
-
const {
|
|
239
|
-
|
|
240
|
-
const roomRef = ref(this.rtdb, `presence/${this.room}`);
|
|
241
|
-
|
|
242
|
-
const unsubscribe = onValue(roomRef, (snapshot) => {
|
|
243
|
-
const peers = snapshot.val() || {};
|
|
240
|
+
const { collection, query, where, onSnapshot } = this.firebase;
|
|
244
241
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.peers.set(peerId, data);
|
|
249
|
-
this.emit('peer-discovered', { peerId, ...data });
|
|
250
|
-
}
|
|
251
|
-
}
|
|
242
|
+
// Query peers in same room that were active in last 2 minutes
|
|
243
|
+
const peersRef = collection(this.db, SIGNALING_PATHS.peers);
|
|
244
|
+
const q = query(peersRef, where('room', '==', this.room));
|
|
252
245
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
246
|
+
const unsubscribe = onSnapshot(q, (snapshot) => {
|
|
247
|
+
const now = Date.now();
|
|
248
|
+
const staleThreshold = 2 * 60 * 1000; // 2 minutes
|
|
249
|
+
|
|
250
|
+
snapshot.docChanges().forEach((change) => {
|
|
251
|
+
const data = change.doc.data();
|
|
252
|
+
const peerId = change.doc.id;
|
|
253
|
+
|
|
254
|
+
if (peerId === this.peerId) return; // Skip self
|
|
255
|
+
|
|
256
|
+
if (change.type === 'added' || change.type === 'modified') {
|
|
257
|
+
// Check if peer is still active (lastSeen within threshold)
|
|
258
|
+
const lastSeen = data.lastSeen?.toMillis?.() || 0;
|
|
259
|
+
if (now - lastSeen < staleThreshold) {
|
|
260
|
+
if (!this.peers.has(peerId)) {
|
|
261
|
+
this.peers.set(peerId, data);
|
|
262
|
+
this.emit('peer-discovered', { peerId, ...data });
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
// Peer is stale
|
|
266
|
+
if (this.peers.has(peerId)) {
|
|
267
|
+
this.peers.delete(peerId);
|
|
268
|
+
this.emit('peer-left', { peerId });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} else if (change.type === 'removed') {
|
|
272
|
+
if (this.peers.has(peerId)) {
|
|
273
|
+
this.peers.delete(peerId);
|
|
274
|
+
this.emit('peer-left', { peerId });
|
|
275
|
+
}
|
|
258
276
|
}
|
|
259
|
-
}
|
|
277
|
+
});
|
|
260
278
|
});
|
|
261
279
|
|
|
262
280
|
this.unsubscribers.push(unsubscribe);
|
|
@@ -370,17 +388,27 @@ export class FirebaseSignaling extends EventEmitter {
|
|
|
370
388
|
* Disconnect and cleanup
|
|
371
389
|
*/
|
|
372
390
|
async disconnect() {
|
|
391
|
+
// Stop heartbeat
|
|
392
|
+
if (this._heartbeatInterval) {
|
|
393
|
+
clearInterval(this._heartbeatInterval);
|
|
394
|
+
this._heartbeatInterval = null;
|
|
395
|
+
}
|
|
396
|
+
|
|
373
397
|
// Unsubscribe from all listeners
|
|
374
398
|
for (const unsub of this.unsubscribers) {
|
|
375
399
|
if (typeof unsub === 'function') unsub();
|
|
376
400
|
}
|
|
377
401
|
this.unsubscribers = [];
|
|
378
402
|
|
|
379
|
-
// Remove presence
|
|
380
|
-
if (this.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
403
|
+
// Remove presence from Firestore
|
|
404
|
+
if (this.db && this.firebase) {
|
|
405
|
+
try {
|
|
406
|
+
const { doc, deleteDoc } = this.firebase;
|
|
407
|
+
const presenceRef = doc(this.db, SIGNALING_PATHS.peers, this.peerId);
|
|
408
|
+
await deleteDoc(presenceRef);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
// Ignore cleanup errors
|
|
411
|
+
}
|
|
384
412
|
}
|
|
385
413
|
|
|
386
414
|
this.isConnected = false;
|
package/p2p.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruvector/edge-net",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Distributed compute intelligence network with AI agents and workers - contribute browser compute, spawn distributed AI agents, earn credits. Features Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, ONNX inference, WebRTC signaling, CRDT ledger, and multi-agent workflows.",
|
|
6
6
|
"main": "ruvector_edge_net.js",
|
|
@@ -189,14 +189,7 @@
|
|
|
189
189
|
"dependencies": {
|
|
190
190
|
"@ruvector/ruvllm": "^0.2.3",
|
|
191
191
|
"@xenova/transformers": "^2.17.2",
|
|
192
|
+
"firebase": "^10.14.1",
|
|
192
193
|
"ws": "^8.18.3"
|
|
193
|
-
},
|
|
194
|
-
"peerDependencies": {
|
|
195
|
-
"firebase": "^10.0.0"
|
|
196
|
-
},
|
|
197
|
-
"peerDependenciesMeta": {
|
|
198
|
-
"firebase": {
|
|
199
|
-
"optional": true
|
|
200
|
-
}
|
|
201
194
|
}
|
|
202
195
|
}
|