@hivemind-os/collective-relay 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/dist/index.d.ts +425 -0
- package/dist/index.js +1859 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import { SimpleKeypair, RelayRegistryClient } from '@hivemind-os/collective-core';
|
|
3
|
+
import { DID, PaymentRail } from '@hivemind-os/collective-types';
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
import WebSocket from 'ws';
|
|
6
|
+
|
|
7
|
+
interface RelayConfig {
|
|
8
|
+
host: string;
|
|
9
|
+
port: number;
|
|
10
|
+
identity: {
|
|
11
|
+
keyPath: string;
|
|
12
|
+
};
|
|
13
|
+
fees: {
|
|
14
|
+
basePercentage: number;
|
|
15
|
+
minimumMist: bigint;
|
|
16
|
+
};
|
|
17
|
+
limits: {
|
|
18
|
+
maxConnections: number;
|
|
19
|
+
maxRequestsPerSecond: number;
|
|
20
|
+
taskTimeoutMs: number;
|
|
21
|
+
heartbeatIntervalMs: number;
|
|
22
|
+
heartbeatTimeoutMs: number;
|
|
23
|
+
authNonceTtlMs: number;
|
|
24
|
+
};
|
|
25
|
+
cors?: {
|
|
26
|
+
allowedOrigins: string[];
|
|
27
|
+
};
|
|
28
|
+
sui?: {
|
|
29
|
+
rpcUrl: string;
|
|
30
|
+
packageId: string;
|
|
31
|
+
};
|
|
32
|
+
relayRegistry?: {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
relayId?: string;
|
|
35
|
+
stakePositionId?: string;
|
|
36
|
+
endpoint?: string;
|
|
37
|
+
capabilities: string[];
|
|
38
|
+
region?: string;
|
|
39
|
+
routingFeeBps?: number;
|
|
40
|
+
heartbeatIntervalMs: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
declare function getDefaultRelayConfig(baseDir?: string): RelayConfig;
|
|
44
|
+
declare function loadRelayConfig(overrides?: Partial<RelayConfig>): RelayConfig;
|
|
45
|
+
declare function validateRelayConfig(config: RelayConfig): void;
|
|
46
|
+
|
|
47
|
+
interface HealthMonitorStatus {
|
|
48
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
49
|
+
uptime: number;
|
|
50
|
+
connectedProviders: number;
|
|
51
|
+
activeRequests: number;
|
|
52
|
+
totalRequestsServed: number;
|
|
53
|
+
averageLatencyMs: number;
|
|
54
|
+
}
|
|
55
|
+
interface HealthMonitorOptions {
|
|
56
|
+
now?: () => number;
|
|
57
|
+
getConnectedProviders?: () => number;
|
|
58
|
+
}
|
|
59
|
+
declare class HealthMonitor {
|
|
60
|
+
private readonly options;
|
|
61
|
+
private readonly now;
|
|
62
|
+
private readonly startedAt;
|
|
63
|
+
private activeRequests;
|
|
64
|
+
private connectedProviders;
|
|
65
|
+
private totalRequestsServed;
|
|
66
|
+
private totalLatencyMs;
|
|
67
|
+
constructor(options?: HealthMonitorOptions);
|
|
68
|
+
setConnectedProviders(count: number): void;
|
|
69
|
+
beginRequest(): {
|
|
70
|
+
finish: () => void;
|
|
71
|
+
};
|
|
72
|
+
recordRequest(latencyMs: number): void;
|
|
73
|
+
getStatus(): HealthMonitorStatus;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
declare class RelayIdentity {
|
|
77
|
+
readonly keyPath: string;
|
|
78
|
+
readonly keypair: SimpleKeypair;
|
|
79
|
+
readonly did: DID;
|
|
80
|
+
private constructor();
|
|
81
|
+
static load(keyPath: string): RelayIdentity;
|
|
82
|
+
signPayload(payload: string): string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface AuthMessage {
|
|
86
|
+
type: 'auth';
|
|
87
|
+
did: DID;
|
|
88
|
+
nonce: string;
|
|
89
|
+
signature: string;
|
|
90
|
+
capabilities: string[];
|
|
91
|
+
}
|
|
92
|
+
interface HeartbeatMessage {
|
|
93
|
+
type: 'heartbeat';
|
|
94
|
+
sessionId: string;
|
|
95
|
+
}
|
|
96
|
+
interface TaskResultMessage {
|
|
97
|
+
type: 'task_result';
|
|
98
|
+
sessionId: string;
|
|
99
|
+
taskId: string;
|
|
100
|
+
sequence: number;
|
|
101
|
+
result: unknown;
|
|
102
|
+
}
|
|
103
|
+
interface TaskProgressMessage {
|
|
104
|
+
type: 'task_progress';
|
|
105
|
+
sessionId: string;
|
|
106
|
+
taskId: string;
|
|
107
|
+
sequence: number;
|
|
108
|
+
progress: number;
|
|
109
|
+
message?: string;
|
|
110
|
+
}
|
|
111
|
+
interface TaskChunkMessage {
|
|
112
|
+
type: 'task_chunk';
|
|
113
|
+
sessionId: string;
|
|
114
|
+
taskId: string;
|
|
115
|
+
sequence: number;
|
|
116
|
+
data: string;
|
|
117
|
+
}
|
|
118
|
+
interface TaskErrorMessage {
|
|
119
|
+
type: 'task_error';
|
|
120
|
+
sessionId: string;
|
|
121
|
+
taskId: string;
|
|
122
|
+
sequence: number;
|
|
123
|
+
error: {
|
|
124
|
+
code: string;
|
|
125
|
+
message: string;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
type ProviderMessage = AuthMessage | HeartbeatMessage | TaskResultMessage | TaskProgressMessage | TaskChunkMessage | TaskErrorMessage;
|
|
129
|
+
interface AuthOkMessage {
|
|
130
|
+
type: 'auth_ok';
|
|
131
|
+
sessionId: string;
|
|
132
|
+
relayDid: string;
|
|
133
|
+
}
|
|
134
|
+
interface AuthFailMessage {
|
|
135
|
+
type: 'auth_fail';
|
|
136
|
+
reason: string;
|
|
137
|
+
}
|
|
138
|
+
interface TaskRequestMessage {
|
|
139
|
+
type: 'task_request';
|
|
140
|
+
sessionId: string;
|
|
141
|
+
taskId: string;
|
|
142
|
+
capability: string;
|
|
143
|
+
input: unknown;
|
|
144
|
+
requesterDid: DID;
|
|
145
|
+
sequence: number;
|
|
146
|
+
}
|
|
147
|
+
interface HeartbeatAckMessage {
|
|
148
|
+
type: 'heartbeat_ack';
|
|
149
|
+
}
|
|
150
|
+
type RelayMessage = AuthOkMessage | AuthFailMessage | TaskRequestMessage | HeartbeatAckMessage;
|
|
151
|
+
interface TaskRequest {
|
|
152
|
+
requesterDid: DID;
|
|
153
|
+
capability: string;
|
|
154
|
+
input: unknown;
|
|
155
|
+
providerDid?: DID;
|
|
156
|
+
taskId?: string;
|
|
157
|
+
timeoutMs?: number;
|
|
158
|
+
}
|
|
159
|
+
interface TaskResponse {
|
|
160
|
+
taskId: string;
|
|
161
|
+
providerDid: DID;
|
|
162
|
+
sequence: number;
|
|
163
|
+
result: unknown;
|
|
164
|
+
}
|
|
165
|
+
type TaskStreamEvent = {
|
|
166
|
+
type: 'progress';
|
|
167
|
+
taskId: string;
|
|
168
|
+
sequence: number;
|
|
169
|
+
progress: number;
|
|
170
|
+
message?: string;
|
|
171
|
+
} | {
|
|
172
|
+
type: 'chunk';
|
|
173
|
+
taskId: string;
|
|
174
|
+
sequence: number;
|
|
175
|
+
data: string;
|
|
176
|
+
} | {
|
|
177
|
+
type: 'result';
|
|
178
|
+
taskId: string;
|
|
179
|
+
sequence: number;
|
|
180
|
+
result: unknown;
|
|
181
|
+
};
|
|
182
|
+
type ProviderTaskMessage = Exclude<ProviderMessage, AuthMessage | HeartbeatMessage>;
|
|
183
|
+
declare function normalizeCapability(capability: string): string;
|
|
184
|
+
declare function createAuthPayload(message: Pick<AuthMessage, 'did' | 'nonce' | 'capabilities'>): string;
|
|
185
|
+
declare function serializeRelayMessage(message: RelayMessage): string;
|
|
186
|
+
declare function parseProviderMessage(payload: string | Buffer | ArrayBuffer | Buffer[]): ProviderMessage | null;
|
|
187
|
+
declare function parseRelayMessage(payload: string | Buffer | ArrayBuffer | Buffer[]): RelayMessage | null;
|
|
188
|
+
|
|
189
|
+
interface ProviderSession {
|
|
190
|
+
sessionId: string;
|
|
191
|
+
providerDid: DID;
|
|
192
|
+
ws: WebSocket;
|
|
193
|
+
capabilities: string[];
|
|
194
|
+
connectedAt: number;
|
|
195
|
+
lastHeartbeat: number;
|
|
196
|
+
sequenceCounter: number;
|
|
197
|
+
}
|
|
198
|
+
interface ProviderInfo {
|
|
199
|
+
sessionId: string;
|
|
200
|
+
providerDid: DID;
|
|
201
|
+
capabilities: string[];
|
|
202
|
+
connectedAt: number;
|
|
203
|
+
lastHeartbeat: number;
|
|
204
|
+
}
|
|
205
|
+
interface SessionManagerOptions {
|
|
206
|
+
maxConnections: number;
|
|
207
|
+
heartbeatTimeoutMs: number;
|
|
208
|
+
authNonceTtlMs?: number;
|
|
209
|
+
now?: () => number;
|
|
210
|
+
}
|
|
211
|
+
declare class SessionManager extends EventEmitter {
|
|
212
|
+
private readonly options;
|
|
213
|
+
private readonly sessions;
|
|
214
|
+
private readonly sessionsByDid;
|
|
215
|
+
private readonly capabilityIndex;
|
|
216
|
+
private readonly inboundSequences;
|
|
217
|
+
private readonly capabilityCursor;
|
|
218
|
+
private readonly recentAuthNonces;
|
|
219
|
+
private readonly now;
|
|
220
|
+
constructor(options: SessionManagerOptions);
|
|
221
|
+
registerSession(ws: WebSocket, authMessage: AuthMessage): ProviderSession;
|
|
222
|
+
findProvider(capability: string, preferredDid?: DID): ProviderSession | null;
|
|
223
|
+
removeSession(sessionId: string): void;
|
|
224
|
+
disconnectSession(sessionId: string, code?: number, reason?: string): void;
|
|
225
|
+
disconnectAllSessions(code?: number, reason?: string): void;
|
|
226
|
+
handleHeartbeat(sessionId: string): void;
|
|
227
|
+
sweepExpiredSessions(): string[];
|
|
228
|
+
getConnectedProviders(): ProviderInfo[];
|
|
229
|
+
getSession(sessionId: string): ProviderSession | null;
|
|
230
|
+
getSessionByDid(did: DID, capability?: string): ProviderSession | null;
|
|
231
|
+
getSessionCount(): number;
|
|
232
|
+
nextSequence(sessionId: string): number;
|
|
233
|
+
validateIncomingSequence(sessionId: string, sequence: number): boolean;
|
|
234
|
+
private verifyAuthMessage;
|
|
235
|
+
private markAuthNonceUsed;
|
|
236
|
+
private pruneRecentAuthNonces;
|
|
237
|
+
private getAuthNonceKey;
|
|
238
|
+
private getAuthNonceTtlMs;
|
|
239
|
+
private addDidIndex;
|
|
240
|
+
private removeDidIndex;
|
|
241
|
+
private addCapabilities;
|
|
242
|
+
private removeCapabilities;
|
|
243
|
+
private toProviderInfo;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface RelayFeeSchedule {
|
|
247
|
+
basePercentage: number;
|
|
248
|
+
minimumMist: bigint;
|
|
249
|
+
}
|
|
250
|
+
interface RelayFeeBreakdown {
|
|
251
|
+
relayFee: bigint;
|
|
252
|
+
totalPrice: bigint;
|
|
253
|
+
}
|
|
254
|
+
declare function calculateRelayFee(basePrice: bigint, feeSchedule: RelayFeeSchedule): RelayFeeBreakdown;
|
|
255
|
+
|
|
256
|
+
interface PaymentChallenge {
|
|
257
|
+
rail: PaymentRail;
|
|
258
|
+
paymentAddress: string;
|
|
259
|
+
amount: string;
|
|
260
|
+
currency: string;
|
|
261
|
+
network: string;
|
|
262
|
+
relayFee: string;
|
|
263
|
+
expiresAt: number;
|
|
264
|
+
nonce: string;
|
|
265
|
+
asset?: string;
|
|
266
|
+
extra?: Record<string, string>;
|
|
267
|
+
}
|
|
268
|
+
interface PaymentVerification {
|
|
269
|
+
accepted: boolean;
|
|
270
|
+
settlementReference?: string;
|
|
271
|
+
relayFee?: string;
|
|
272
|
+
totalPrice?: string;
|
|
273
|
+
payer?: string;
|
|
274
|
+
reason?: string;
|
|
275
|
+
}
|
|
276
|
+
interface PaymentGateOptions {
|
|
277
|
+
relayDid: string;
|
|
278
|
+
feeSchedule: RelayFeeSchedule;
|
|
279
|
+
paymentAddress?: string;
|
|
280
|
+
evmPaymentAddress?: string;
|
|
281
|
+
defaultRail?: PaymentRail;
|
|
282
|
+
challengeTtlMs?: number;
|
|
283
|
+
now?: () => number;
|
|
284
|
+
nonceFactory?: () => string;
|
|
285
|
+
basePriceResolver?: (capability: string, provider: ProviderSession) => bigint;
|
|
286
|
+
verifyPaymentProof?: (paymentHeader: string, challenge: PaymentChallenge) => Promise<PaymentVerification>;
|
|
287
|
+
}
|
|
288
|
+
declare class PaymentGate {
|
|
289
|
+
private readonly options;
|
|
290
|
+
private readonly activeChallenges;
|
|
291
|
+
private readonly consumedProofs;
|
|
292
|
+
private readonly now;
|
|
293
|
+
private readonly nonceFactory;
|
|
294
|
+
private readonly basePriceResolver;
|
|
295
|
+
readonly defaultRail: PaymentRail;
|
|
296
|
+
constructor(options: PaymentGateOptions);
|
|
297
|
+
generate402Challenge(rail: PaymentRail, capability: string, provider: ProviderSession): PaymentChallenge;
|
|
298
|
+
getChallenge(nonce: string): PaymentChallenge | null;
|
|
299
|
+
isChallengeExpired(challenge: PaymentChallenge): boolean;
|
|
300
|
+
verifyPayment(paymentHeader: string, challenge: PaymentChallenge): Promise<PaymentVerification>;
|
|
301
|
+
calculateFee(basePrice: bigint): RelayFeeBreakdown;
|
|
302
|
+
pruneExpiredChallenges(): number;
|
|
303
|
+
private pruneConsumedProofs;
|
|
304
|
+
private createSuiChallenge;
|
|
305
|
+
private createX402Challenge;
|
|
306
|
+
private verifyChallenge;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
interface RelayRegistryRuntimeInfo {
|
|
310
|
+
enabled: boolean;
|
|
311
|
+
registered: boolean;
|
|
312
|
+
relayId?: string;
|
|
313
|
+
operator?: string;
|
|
314
|
+
endpoint?: string;
|
|
315
|
+
stakePositionId?: string;
|
|
316
|
+
capabilities?: string[];
|
|
317
|
+
region?: string;
|
|
318
|
+
status?: 'ACTIVE' | 'INACTIVE' | 'SLASHED';
|
|
319
|
+
routingFeeBps?: number;
|
|
320
|
+
lastHeartbeat?: number;
|
|
321
|
+
totalRouted?: number;
|
|
322
|
+
totalFeesEarnedMist?: string;
|
|
323
|
+
lastError?: string;
|
|
324
|
+
}
|
|
325
|
+
interface RelayRegistryRuntime {
|
|
326
|
+
start: (serverAddress: string) => Promise<void>;
|
|
327
|
+
stop: () => Promise<void>;
|
|
328
|
+
recordRouting: (feeAmountMist: bigint) => Promise<void>;
|
|
329
|
+
getInfo: () => RelayRegistryRuntimeInfo;
|
|
330
|
+
}
|
|
331
|
+
declare class RelayRegistryService implements RelayRegistryRuntime {
|
|
332
|
+
private readonly config;
|
|
333
|
+
private readonly identity;
|
|
334
|
+
private readonly enabled;
|
|
335
|
+
private readonly signer;
|
|
336
|
+
private readonly client?;
|
|
337
|
+
private heartbeatTimer?;
|
|
338
|
+
private state;
|
|
339
|
+
constructor(config: RelayConfig, identity: RelayIdentity, client?: RelayRegistryClient);
|
|
340
|
+
start(serverAddress: string): Promise<void>;
|
|
341
|
+
stop(): Promise<void>;
|
|
342
|
+
recordRouting(feeAmountMist: bigint): Promise<void>;
|
|
343
|
+
getInfo(): RelayRegistryRuntimeInfo;
|
|
344
|
+
private sendHeartbeat;
|
|
345
|
+
private resolveExistingRelay;
|
|
346
|
+
private refreshRelay;
|
|
347
|
+
private applyRelay;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
interface RelayRouterOptions {
|
|
351
|
+
sessionManager: SessionManager;
|
|
352
|
+
taskTimeoutMs: number;
|
|
353
|
+
}
|
|
354
|
+
declare class RelayRouteError extends Error {
|
|
355
|
+
readonly code: string;
|
|
356
|
+
readonly statusCode: number;
|
|
357
|
+
readonly retryable: boolean;
|
|
358
|
+
constructor(code: string, message: string, statusCode: number, retryable?: boolean);
|
|
359
|
+
}
|
|
360
|
+
declare class RelayRouter {
|
|
361
|
+
private readonly options;
|
|
362
|
+
private readonly pendingTasks;
|
|
363
|
+
constructor(options: RelayRouterOptions);
|
|
364
|
+
routeTask(request: TaskRequest): Promise<TaskResponse>;
|
|
365
|
+
routeStreamingTask(request: TaskRequest, onChunk: (chunk: TaskStreamEvent) => void): Promise<TaskResponse>;
|
|
366
|
+
routeMulti(request: TaskRequest, providerDids: DID[]): Promise<TaskResponse[]>;
|
|
367
|
+
close(): void;
|
|
368
|
+
handleProviderMessage(sessionId: string, message: ProviderTaskMessage): void;
|
|
369
|
+
private dispatch;
|
|
370
|
+
private emitTaskEvent;
|
|
371
|
+
private completeTask;
|
|
372
|
+
private rejectSessionTasks;
|
|
373
|
+
private findSession;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
interface RelayServer {
|
|
377
|
+
app: FastifyInstance;
|
|
378
|
+
config: RelayConfig;
|
|
379
|
+
identity: RelayIdentity;
|
|
380
|
+
sessionManager: SessionManager;
|
|
381
|
+
router: RelayRouter;
|
|
382
|
+
paymentGate: PaymentGate;
|
|
383
|
+
healthMonitor: HealthMonitor;
|
|
384
|
+
relayRegistry?: RelayRegistryRuntime;
|
|
385
|
+
start: () => Promise<string>;
|
|
386
|
+
}
|
|
387
|
+
interface RelayServerOptions {
|
|
388
|
+
relayRegistry?: RelayRegistryRuntime;
|
|
389
|
+
}
|
|
390
|
+
declare function createRelayServer(config: RelayConfig, options?: RelayServerOptions): Promise<RelayServer>;
|
|
391
|
+
|
|
392
|
+
interface MeshRequestContext {
|
|
393
|
+
requestId: string;
|
|
394
|
+
requesterDid?: DID;
|
|
395
|
+
targetProviderDid?: DID;
|
|
396
|
+
timestamp?: string;
|
|
397
|
+
signature?: string;
|
|
398
|
+
paymentSignature?: string;
|
|
399
|
+
paymentNonce?: string;
|
|
400
|
+
paymentRail?: PaymentRail;
|
|
401
|
+
sessionId?: string;
|
|
402
|
+
sequence?: number;
|
|
403
|
+
}
|
|
404
|
+
declare function registerMeshMiddleware(app: FastifyInstance, relayDid: string): void;
|
|
405
|
+
declare function getMeshRequestContext(request: FastifyRequest): MeshRequestContext;
|
|
406
|
+
declare function applyMeshResponseHeaders(reply: FastifyReply, relayDid: string, requestId: string): void;
|
|
407
|
+
|
|
408
|
+
interface RelayRouteDependencies {
|
|
409
|
+
relayDid: DID;
|
|
410
|
+
version: string;
|
|
411
|
+
config: RelayConfig;
|
|
412
|
+
sessionManager: SessionManager;
|
|
413
|
+
router: RelayRouter;
|
|
414
|
+
paymentGate: PaymentGate;
|
|
415
|
+
healthMonitor: HealthMonitor;
|
|
416
|
+
relayRegistry?: RelayRegistryRuntime;
|
|
417
|
+
}
|
|
418
|
+
declare function registerRelayRoutes(app: FastifyInstance, deps: RelayRouteDependencies): Promise<void>;
|
|
419
|
+
|
|
420
|
+
declare function startRelayServer(): Promise<{
|
|
421
|
+
relay: RelayServer;
|
|
422
|
+
address: string;
|
|
423
|
+
}>;
|
|
424
|
+
|
|
425
|
+
export { type AuthFailMessage, type AuthMessage, type AuthOkMessage, HealthMonitor, type HealthMonitorOptions, type HealthMonitorStatus, type HeartbeatAckMessage, type HeartbeatMessage, type MeshRequestContext, type PaymentChallenge, PaymentGate, type PaymentGateOptions, type PaymentVerification, type ProviderInfo, type ProviderMessage, type ProviderSession, type ProviderTaskMessage, type RelayConfig, type RelayFeeBreakdown, type RelayFeeSchedule, RelayIdentity, type RelayMessage, type RelayRegistryRuntime, type RelayRegistryRuntimeInfo, RelayRegistryService, type RelayRouteDependencies, RelayRouteError, RelayRouter, type RelayServer, type RelayServerOptions, SessionManager, type SessionManagerOptions, type TaskChunkMessage, type TaskErrorMessage, type TaskProgressMessage, type TaskRequest, type TaskRequestMessage, type TaskResponse, type TaskResultMessage, type TaskStreamEvent, applyMeshResponseHeaders, calculateRelayFee, createAuthPayload, createRelayServer, getDefaultRelayConfig, getMeshRequestContext, loadRelayConfig, normalizeCapability, parseProviderMessage, parseRelayMessage, registerMeshMiddleware, registerRelayRoutes, serializeRelayMessage, startRelayServer, validateRelayConfig };
|