@antseed/node 0.1.0 → 0.1.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.
Files changed (130) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/interfaces/seller-provider.d.ts +13 -1
  6. package/dist/interfaces/seller-provider.d.ts.map +1 -1
  7. package/dist/node.d.ts +13 -3
  8. package/dist/node.d.ts.map +1 -1
  9. package/dist/node.js +123 -15
  10. package/dist/node.js.map +1 -1
  11. package/dist/proxy/proxy-mux.d.ts +3 -1
  12. package/dist/proxy/proxy-mux.d.ts.map +1 -1
  13. package/dist/proxy/proxy-mux.js +9 -5
  14. package/dist/proxy/proxy-mux.js.map +1 -1
  15. package/dist/types/http.d.ts +1 -0
  16. package/dist/types/http.d.ts.map +1 -1
  17. package/dist/types/http.js +1 -1
  18. package/dist/types/http.js.map +1 -1
  19. package/package.json +14 -10
  20. package/contracts/AntseedEscrow.sol +0 -310
  21. package/contracts/MockUSDC.sol +0 -64
  22. package/contracts/README.md +0 -102
  23. package/src/config/encryption.test.ts +0 -49
  24. package/src/config/encryption.ts +0 -53
  25. package/src/config/plugin-config-manager.test.ts +0 -92
  26. package/src/config/plugin-config-manager.ts +0 -153
  27. package/src/config/plugin-loader.ts +0 -90
  28. package/src/discovery/announcer.ts +0 -169
  29. package/src/discovery/bootstrap.ts +0 -57
  30. package/src/discovery/default-metadata-resolver.ts +0 -18
  31. package/src/discovery/dht-health.ts +0 -136
  32. package/src/discovery/dht-node.ts +0 -191
  33. package/src/discovery/http-metadata-resolver.ts +0 -47
  34. package/src/discovery/index.ts +0 -15
  35. package/src/discovery/metadata-codec.ts +0 -453
  36. package/src/discovery/metadata-resolver.ts +0 -7
  37. package/src/discovery/metadata-server.ts +0 -73
  38. package/src/discovery/metadata-validator.ts +0 -172
  39. package/src/discovery/peer-lookup.ts +0 -122
  40. package/src/discovery/peer-metadata.ts +0 -34
  41. package/src/discovery/peer-selector.ts +0 -134
  42. package/src/discovery/profile-manager.ts +0 -131
  43. package/src/discovery/profile-search.ts +0 -100
  44. package/src/discovery/reputation-verifier.ts +0 -54
  45. package/src/index.ts +0 -61
  46. package/src/interfaces/buyer-router.ts +0 -21
  47. package/src/interfaces/plugin.ts +0 -36
  48. package/src/interfaces/seller-provider.ts +0 -81
  49. package/src/metering/index.ts +0 -6
  50. package/src/metering/receipt-generator.ts +0 -105
  51. package/src/metering/receipt-verifier.ts +0 -102
  52. package/src/metering/session-tracker.ts +0 -145
  53. package/src/metering/storage.ts +0 -600
  54. package/src/metering/token-counter.ts +0 -127
  55. package/src/metering/usage-aggregator.ts +0 -236
  56. package/src/node.ts +0 -1698
  57. package/src/p2p/connection-auth.ts +0 -152
  58. package/src/p2p/connection-manager.ts +0 -916
  59. package/src/p2p/handshake.ts +0 -162
  60. package/src/p2p/ice-config.ts +0 -59
  61. package/src/p2p/identity.ts +0 -110
  62. package/src/p2p/index.ts +0 -11
  63. package/src/p2p/keepalive.ts +0 -118
  64. package/src/p2p/message-protocol.ts +0 -171
  65. package/src/p2p/nat-traversal.ts +0 -169
  66. package/src/p2p/payment-codec.ts +0 -165
  67. package/src/p2p/payment-mux.ts +0 -153
  68. package/src/p2p/reconnect.ts +0 -117
  69. package/src/payments/balance-manager.ts +0 -77
  70. package/src/payments/buyer-payment-manager.ts +0 -414
  71. package/src/payments/disputes.ts +0 -72
  72. package/src/payments/evm/escrow-client.ts +0 -263
  73. package/src/payments/evm/keypair.ts +0 -31
  74. package/src/payments/evm/signatures.ts +0 -103
  75. package/src/payments/evm/wallet.ts +0 -42
  76. package/src/payments/index.ts +0 -50
  77. package/src/payments/settlement.ts +0 -40
  78. package/src/payments/types.ts +0 -79
  79. package/src/proxy/index.ts +0 -3
  80. package/src/proxy/provider-detection.ts +0 -78
  81. package/src/proxy/proxy-mux.ts +0 -173
  82. package/src/proxy/request-codec.ts +0 -294
  83. package/src/reputation/index.ts +0 -6
  84. package/src/reputation/rating-manager.ts +0 -118
  85. package/src/reputation/report-manager.ts +0 -91
  86. package/src/reputation/trust-engine.ts +0 -120
  87. package/src/reputation/trust-score.ts +0 -74
  88. package/src/reputation/uptime-tracker.ts +0 -155
  89. package/src/routing/default-router.ts +0 -75
  90. package/src/types/bittorrent-dht.d.ts +0 -19
  91. package/src/types/buyer.ts +0 -37
  92. package/src/types/capability.ts +0 -34
  93. package/src/types/connection.ts +0 -29
  94. package/src/types/http.ts +0 -20
  95. package/src/types/index.ts +0 -14
  96. package/src/types/metering.ts +0 -175
  97. package/src/types/nat-api.d.ts +0 -29
  98. package/src/types/peer-profile.ts +0 -25
  99. package/src/types/peer.ts +0 -62
  100. package/src/types/plugin-config.ts +0 -31
  101. package/src/types/protocol.ts +0 -162
  102. package/src/types/provider.ts +0 -40
  103. package/src/types/rating.ts +0 -23
  104. package/src/types/report.ts +0 -30
  105. package/src/types/seller.ts +0 -38
  106. package/src/types/staking.ts +0 -23
  107. package/src/utils/debug.ts +0 -30
  108. package/src/utils/hex.ts +0 -14
  109. package/tests/balance-manager.test.ts +0 -156
  110. package/tests/bootstrap.test.ts +0 -108
  111. package/tests/buyer-payment-manager.test.ts +0 -358
  112. package/tests/connection-auth.test.ts +0 -87
  113. package/tests/default-router.test.ts +0 -148
  114. package/tests/evm-keypair.test.ts +0 -173
  115. package/tests/identity.test.ts +0 -133
  116. package/tests/message-protocol.test.ts +0 -212
  117. package/tests/metadata-codec.test.ts +0 -165
  118. package/tests/metadata-validator.test.ts +0 -261
  119. package/tests/metering-storage.test.ts +0 -244
  120. package/tests/payment-codec.test.ts +0 -95
  121. package/tests/payment-mux.test.ts +0 -191
  122. package/tests/peer-selector.test.ts +0 -184
  123. package/tests/provider-detection.test.ts +0 -107
  124. package/tests/proxy-mux-security.test.ts +0 -38
  125. package/tests/receipt.test.ts +0 -215
  126. package/tests/reputation-integration.test.ts +0 -195
  127. package/tests/request-codec.test.ts +0 -144
  128. package/tests/token-counter.test.ts +0 -122
  129. package/tsconfig.json +0 -9
  130. package/vitest.config.ts +0 -7
@@ -1,453 +0,0 @@
1
- import type { PeerMetadata } from "./peer-metadata.js";
2
- import type { PeerOffering } from "../types/capability.js";
3
- import { hexToBytes, bytesToHex } from "../utils/hex.js";
4
- import { toPeerId } from "../types/peer.js";
5
-
6
- /**
7
- * Encode metadata into binary format:
8
- * [version:1][peerId:32][regionLen:1][region:N][timestamp:8 BigUint64][providerCount:1]
9
- * for each provider:
10
- * [providerLen:1][provider:N][modelCount:1][models...]
11
- * [defaultInputPrice:4][defaultOutputPrice:4]
12
- * [modelPricingCount:1][modelPricingEntries...]
13
- * [maxConcurrency:2][currentLoad:2]
14
- * modelPricingEntry: [modelLen:1][model:N][inputPrice:4][outputPrice:4]
15
- * [signature:64]
16
- */
17
- export function encodeMetadata(metadata: PeerMetadata): Uint8Array {
18
- const bodyBytes = encodeBody(metadata);
19
- const signatureBytes = hexToBytes(metadata.signature);
20
-
21
- const result = new Uint8Array(bodyBytes.length + signatureBytes.length);
22
- result.set(bodyBytes, 0);
23
- result.set(signatureBytes, bodyBytes.length);
24
- return result;
25
- }
26
-
27
- /**
28
- * Encode metadata without signature, for signing purposes.
29
- */
30
- export function encodeMetadataForSigning(metadata: PeerMetadata): Uint8Array {
31
- return encodeBody(metadata);
32
- }
33
-
34
- function encodeBody(metadata: PeerMetadata): Uint8Array {
35
- const parts: Uint8Array[] = [];
36
-
37
- // version: 1 byte
38
- parts.push(new Uint8Array([metadata.version]));
39
-
40
- // peerId: 32 bytes
41
- parts.push(hexToBytes(metadata.peerId));
42
-
43
- // region: length-prefixed
44
- const regionBytes = new TextEncoder().encode(metadata.region);
45
- parts.push(new Uint8Array([regionBytes.length]));
46
- parts.push(regionBytes);
47
-
48
- // timestamp: 8 bytes BigUint64
49
- const timestampBuf = new ArrayBuffer(8);
50
- const timestampView = new DataView(timestampBuf);
51
- timestampView.setBigUint64(0, BigInt(metadata.timestamp), false);
52
- parts.push(new Uint8Array(timestampBuf));
53
-
54
- // providerCount: 1 byte
55
- parts.push(new Uint8Array([metadata.providers.length]));
56
-
57
- // each provider
58
- for (const p of metadata.providers) {
59
- const providerNameBytes = new TextEncoder().encode(p.provider);
60
- parts.push(new Uint8Array([providerNameBytes.length]));
61
- parts.push(providerNameBytes);
62
-
63
- // modelCount: 1 byte
64
- parts.push(new Uint8Array([p.models.length]));
65
-
66
- // each model: length-prefixed
67
- for (const model of p.models) {
68
- const modelBytes = new TextEncoder().encode(model);
69
- parts.push(new Uint8Array([modelBytes.length]));
70
- parts.push(modelBytes);
71
- }
72
-
73
- // default input price: 4 bytes (float32)
74
- const inputPriceBuf = new ArrayBuffer(4);
75
- new DataView(inputPriceBuf).setFloat32(0, p.defaultPricing.inputUsdPerMillion, false);
76
- parts.push(new Uint8Array(inputPriceBuf));
77
-
78
- // default output price: 4 bytes (float32)
79
- const outputPriceBuf = new ArrayBuffer(4);
80
- new DataView(outputPriceBuf).setFloat32(0, p.defaultPricing.outputUsdPerMillion, false);
81
- parts.push(new Uint8Array(outputPriceBuf));
82
-
83
- // modelPricing entries
84
- const modelPricingEntries = Object.entries(p.modelPricing ?? {}).sort(([a], [b]) =>
85
- a.localeCompare(b),
86
- );
87
- parts.push(new Uint8Array([modelPricingEntries.length]));
88
- for (const [modelName, pricing] of modelPricingEntries) {
89
- const modelNameBytes = new TextEncoder().encode(modelName);
90
- parts.push(new Uint8Array([modelNameBytes.length]));
91
- parts.push(modelNameBytes);
92
-
93
- const modelInputBuf = new ArrayBuffer(4);
94
- new DataView(modelInputBuf).setFloat32(0, pricing.inputUsdPerMillion, false);
95
- parts.push(new Uint8Array(modelInputBuf));
96
-
97
- const modelOutputBuf = new ArrayBuffer(4);
98
- new DataView(modelOutputBuf).setFloat32(0, pricing.outputUsdPerMillion, false);
99
- parts.push(new Uint8Array(modelOutputBuf));
100
- }
101
-
102
- // maxConcurrency: 2 bytes (uint16)
103
- const maxConcBuf = new ArrayBuffer(2);
104
- new DataView(maxConcBuf).setUint16(0, p.maxConcurrency, false);
105
- parts.push(new Uint8Array(maxConcBuf));
106
-
107
- // currentLoad: 2 bytes (uint16)
108
- const loadBuf = new ArrayBuffer(2);
109
- new DataView(loadBuf).setUint16(0, p.currentLoad, false);
110
- parts.push(new Uint8Array(loadBuf));
111
- }
112
-
113
- // offerings
114
- const offerings = metadata.offerings ?? [];
115
- const offeringCountBuf = new ArrayBuffer(2);
116
- new DataView(offeringCountBuf).setUint16(0, offerings.length, false);
117
- parts.push(new Uint8Array(offeringCountBuf));
118
-
119
- const PRICING_UNIT_MAP: Record<string, number> = { token: 0, request: 1, minute: 2, task: 3 };
120
-
121
- for (const o of offerings) {
122
- // capability: length-prefixed (1 byte len)
123
- const capBytes = new TextEncoder().encode(o.capability);
124
- parts.push(new Uint8Array([capBytes.length]));
125
- parts.push(capBytes);
126
-
127
- // name: length-prefixed (1 byte len)
128
- const nameBytes = new TextEncoder().encode(o.name);
129
- parts.push(new Uint8Array([nameBytes.length]));
130
- parts.push(nameBytes);
131
-
132
- // description: length-prefixed (2 byte uint16 len)
133
- const descBytes = new TextEncoder().encode(o.description);
134
- const descLenBuf = new ArrayBuffer(2);
135
- new DataView(descLenBuf).setUint16(0, descBytes.length, false);
136
- parts.push(new Uint8Array(descLenBuf));
137
- parts.push(descBytes);
138
-
139
- // pricingUnit: 1 byte
140
- parts.push(new Uint8Array([PRICING_UNIT_MAP[o.pricing.unit] ?? 0]));
141
-
142
- // pricePerUnit: 4 bytes float32
143
- const priceBuf = new ArrayBuffer(4);
144
- new DataView(priceBuf).setFloat32(0, o.pricing.pricePerUnit, false);
145
- parts.push(new Uint8Array(priceBuf));
146
-
147
- // modelCount: 1 byte, then each model
148
- const models = o.models ?? [];
149
- parts.push(new Uint8Array([models.length]));
150
- for (const model of models) {
151
- const modelBytes = new TextEncoder().encode(model);
152
- parts.push(new Uint8Array([modelBytes.length]));
153
- parts.push(modelBytes);
154
- }
155
- }
156
-
157
- // EVM address: 1 flag byte + 20 address bytes if present
158
- if (metadata.evmAddress) {
159
- parts.push(new Uint8Array([1])); // flag: present
160
- // Strip 0x prefix if present, then decode 20 bytes
161
- const addrHex = metadata.evmAddress.startsWith('0x')
162
- ? metadata.evmAddress.slice(2)
163
- : metadata.evmAddress;
164
- parts.push(hexToBytes(addrHex.toLowerCase().padStart(40, '0')));
165
- } else {
166
- parts.push(new Uint8Array([0])); // flag: absent
167
- }
168
-
169
- // On-chain reputation: 1 flag byte + 10 data bytes (1 reputation + 4 sessionCount + 4 disputeCount + 1 reserved)
170
- if (metadata.onChainReputation !== undefined) {
171
- parts.push(new Uint8Array([1])); // flag: present
172
- const repBuf = new ArrayBuffer(10);
173
- const repView = new DataView(repBuf);
174
- repView.setUint8(0, Math.min(255, Math.max(0, metadata.onChainReputation)));
175
- repView.setUint32(1, metadata.onChainSessionCount ?? 0, false);
176
- repView.setUint32(5, metadata.onChainDisputeCount ?? 0, false);
177
- repView.setUint8(9, 0); // reserved
178
- parts.push(new Uint8Array(repBuf));
179
- } else {
180
- parts.push(new Uint8Array([0])); // flag: absent
181
- }
182
-
183
- // Combine all parts
184
- const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
185
- const result = new Uint8Array(totalLength);
186
- let offset = 0;
187
- for (const part of parts) {
188
- result.set(part, offset);
189
- offset += part.length;
190
- }
191
- return result;
192
- }
193
-
194
- /**
195
- * Decode binary metadata back into PeerMetadata.
196
- */
197
- export function decodeMetadata(data: Uint8Array): PeerMetadata {
198
- function checkBounds(offset: number, needed: number, total: number): void {
199
- if (offset + needed > total) throw new Error('Truncated metadata buffer');
200
- }
201
-
202
- let offset = 0;
203
-
204
- // version: 1 byte
205
- checkBounds(offset, 1, data.length);
206
- const version = data[offset]!;
207
- offset += 1;
208
-
209
- // peerId: 32 bytes
210
- checkBounds(offset, 32, data.length);
211
- const peerIdBytes = data.slice(offset, offset + 32);
212
- const peerId = bytesToHex(peerIdBytes);
213
- offset += 32;
214
-
215
- // region: length-prefixed
216
- checkBounds(offset, 1, data.length);
217
- const regionLen = data[offset]!;
218
- offset += 1;
219
- checkBounds(offset, regionLen, data.length);
220
- const region = new TextDecoder().decode(data.slice(offset, offset + regionLen));
221
- offset += regionLen;
222
-
223
- // timestamp: 8 bytes BigUint64
224
- checkBounds(offset, 8, data.length);
225
- const timestampView = new DataView(data.buffer, data.byteOffset + offset, 8);
226
- const timestamp = Number(timestampView.getBigUint64(0, false));
227
- offset += 8;
228
-
229
- // providerCount: 1 byte
230
- checkBounds(offset, 1, data.length);
231
- const providerCount = data[offset]!;
232
- offset += 1;
233
-
234
- const providers = [];
235
- for (let i = 0; i < providerCount; i++) {
236
- // provider name: length-prefixed
237
- checkBounds(offset, 1, data.length);
238
- const providerLen = data[offset]!;
239
- offset += 1;
240
- checkBounds(offset, providerLen, data.length);
241
- const provider = new TextDecoder().decode(data.slice(offset, offset + providerLen));
242
- offset += providerLen;
243
-
244
- // modelCount: 1 byte
245
- checkBounds(offset, 1, data.length);
246
- const modelCount = data[offset]!;
247
- offset += 1;
248
-
249
- const models: string[] = [];
250
- for (let j = 0; j < modelCount; j++) {
251
- checkBounds(offset, 1, data.length);
252
- const modelLen = data[offset]!;
253
- offset += 1;
254
- checkBounds(offset, modelLen, data.length);
255
- const model = new TextDecoder().decode(data.slice(offset, offset + modelLen));
256
- offset += modelLen;
257
- models.push(model);
258
- }
259
-
260
- // default input price: 4 bytes float32
261
- checkBounds(offset, 4, data.length);
262
- const inputPriceView = new DataView(data.buffer, data.byteOffset + offset, 4);
263
- const defaultInputUsdPerMillion = inputPriceView.getFloat32(0, false);
264
- offset += 4;
265
-
266
- // default output price: 4 bytes float32
267
- checkBounds(offset, 4, data.length);
268
- const outputPriceView = new DataView(data.buffer, data.byteOffset + offset, 4);
269
- const defaultOutputUsdPerMillion = outputPriceView.getFloat32(0, false);
270
- offset += 4;
271
-
272
- // modelPricing entries
273
- checkBounds(offset, 1, data.length);
274
- const modelPricingCount = data[offset]!;
275
- offset += 1;
276
-
277
- const modelPricing: Record<string, { inputUsdPerMillion: number; outputUsdPerMillion: number }> = {};
278
- for (let j = 0; j < modelPricingCount; j++) {
279
- checkBounds(offset, 1, data.length);
280
- const pricedModelLen = data[offset]!;
281
- offset += 1;
282
- checkBounds(offset, pricedModelLen, data.length);
283
- const pricedModelName = new TextDecoder().decode(data.slice(offset, offset + pricedModelLen));
284
- offset += pricedModelLen;
285
-
286
- checkBounds(offset, 4, data.length);
287
- const pricedInputView = new DataView(data.buffer, data.byteOffset + offset, 4);
288
- const inputUsdPerMillion = pricedInputView.getFloat32(0, false);
289
- offset += 4;
290
-
291
- checkBounds(offset, 4, data.length);
292
- const pricedOutputView = new DataView(data.buffer, data.byteOffset + offset, 4);
293
- const outputUsdPerMillion = pricedOutputView.getFloat32(0, false);
294
- offset += 4;
295
-
296
- modelPricing[pricedModelName] = {
297
- inputUsdPerMillion,
298
- outputUsdPerMillion,
299
- };
300
- }
301
-
302
- // maxConcurrency: 2 bytes uint16
303
- checkBounds(offset, 2, data.length);
304
- const maxConcView = new DataView(data.buffer, data.byteOffset + offset, 2);
305
- const maxConcurrency = maxConcView.getUint16(0, false);
306
- offset += 2;
307
-
308
- // currentLoad: 2 bytes uint16
309
- checkBounds(offset, 2, data.length);
310
- const loadView = new DataView(data.buffer, data.byteOffset + offset, 2);
311
- const currentLoad = loadView.getUint16(0, false);
312
- offset += 2;
313
-
314
- providers.push({
315
- provider,
316
- models,
317
- defaultPricing: {
318
- inputUsdPerMillion: defaultInputUsdPerMillion,
319
- outputUsdPerMillion: defaultOutputUsdPerMillion,
320
- },
321
- ...(modelPricingCount > 0 ? { modelPricing } : {}),
322
- maxConcurrency,
323
- currentLoad,
324
- });
325
- }
326
-
327
- // offerings (optional — present if there are remaining bytes before the 64-byte signature)
328
- const PRICING_UNIT_REVERSE: Array<'token' | 'request' | 'minute' | 'task'> = ['token', 'request', 'minute', 'task'];
329
- let offerings: PeerOffering[] | undefined;
330
-
331
- const remainingBeforeSignature = data.length - offset - 64;
332
- if (remainingBeforeSignature >= 2) {
333
- offerings = [];
334
- checkBounds(offset, 2, data.length - 64);
335
- const offeringCountView = new DataView(data.buffer, data.byteOffset + offset, 2);
336
- const offeringCount = offeringCountView.getUint16(0, false);
337
- offset += 2;
338
-
339
- for (let i = 0; i < offeringCount; i++) {
340
- // capability
341
- checkBounds(offset, 1, data.length - 64);
342
- const capLen = data[offset]!;
343
- offset += 1;
344
- checkBounds(offset, capLen, data.length - 64);
345
- const capability = new TextDecoder().decode(data.slice(offset, offset + capLen));
346
- offset += capLen;
347
-
348
- // name
349
- checkBounds(offset, 1, data.length - 64);
350
- const nameLen = data[offset]!;
351
- offset += 1;
352
- checkBounds(offset, nameLen, data.length - 64);
353
- const name = new TextDecoder().decode(data.slice(offset, offset + nameLen));
354
- offset += nameLen;
355
-
356
- // description (uint16 length)
357
- checkBounds(offset, 2, data.length - 64);
358
- const descLenView = new DataView(data.buffer, data.byteOffset + offset, 2);
359
- const descLen = descLenView.getUint16(0, false);
360
- offset += 2;
361
- checkBounds(offset, descLen, data.length - 64);
362
- const description = new TextDecoder().decode(data.slice(offset, offset + descLen));
363
- offset += descLen;
364
-
365
- // pricingUnit: 1 byte
366
- checkBounds(offset, 1, data.length - 64);
367
- const pricingUnitIdx = data[offset]!;
368
- offset += 1;
369
- const unit = PRICING_UNIT_REVERSE[pricingUnitIdx] ?? 'token';
370
-
371
- // pricePerUnit: 4 bytes float32
372
- checkBounds(offset, 4, data.length - 64);
373
- const priceView = new DataView(data.buffer, data.byteOffset + offset, 4);
374
- const pricePerUnit = priceView.getFloat32(0, false);
375
- offset += 4;
376
-
377
- // models
378
- checkBounds(offset, 1, data.length - 64);
379
- const modelCount = data[offset]!;
380
- offset += 1;
381
- const models: string[] = [];
382
- for (let j = 0; j < modelCount; j++) {
383
- checkBounds(offset, 1, data.length - 64);
384
- const modelLen = data[offset]!;
385
- offset += 1;
386
- checkBounds(offset, modelLen, data.length - 64);
387
- const model = new TextDecoder().decode(data.slice(offset, offset + modelLen));
388
- offset += modelLen;
389
- models.push(model);
390
- }
391
-
392
- offerings.push({
393
- capability: capability as PeerOffering['capability'],
394
- name,
395
- description,
396
- models: models.length > 0 ? models : undefined,
397
- pricing: { unit, pricePerUnit, currency: 'USD' },
398
- });
399
- }
400
- }
401
-
402
- // Optional EVM address (flag + 20 bytes) — present if there are enough remaining bytes before signature
403
- let evmAddress: string | undefined;
404
- const remainingBeforeEvmSig = data.length - offset - 64;
405
- if (remainingBeforeEvmSig >= 1) {
406
- const evmFlag = data[offset]!;
407
- offset += 1;
408
- if (evmFlag === 1) {
409
- checkBounds(offset, 20, data.length - 64);
410
- const addrBytes = data.slice(offset, offset + 20);
411
- evmAddress = '0x' + bytesToHex(addrBytes);
412
- offset += 20;
413
- }
414
- }
415
-
416
- // Optional on-chain reputation (flag + 10 bytes)
417
- let onChainReputation: number | undefined;
418
- let onChainSessionCount: number | undefined;
419
- let onChainDisputeCount: number | undefined;
420
- const remainingBeforeRepSig = data.length - offset - 64;
421
- if (remainingBeforeRepSig >= 1) {
422
- const repFlag = data[offset]!;
423
- offset += 1;
424
- if (repFlag === 1) {
425
- checkBounds(offset, 10, data.length - 64);
426
- const repView = new DataView(data.buffer, data.byteOffset + offset, 10);
427
- onChainReputation = repView.getUint8(0);
428
- onChainSessionCount = repView.getUint32(1, false);
429
- onChainDisputeCount = repView.getUint32(5, false);
430
- // byte 9 is reserved
431
- offset += 10;
432
- }
433
- }
434
-
435
- // signature: 64 bytes
436
- checkBounds(offset, 64, data.length);
437
- const signatureBytes = data.slice(offset, offset + 64);
438
- const signature = bytesToHex(signatureBytes);
439
-
440
- return {
441
- peerId: toPeerId(peerId),
442
- version,
443
- providers,
444
- ...(offerings && offerings.length > 0 ? { offerings } : {}),
445
- ...(evmAddress !== undefined ? { evmAddress } : {}),
446
- ...(onChainReputation !== undefined ? { onChainReputation } : {}),
447
- ...(onChainSessionCount !== undefined ? { onChainSessionCount } : {}),
448
- ...(onChainDisputeCount !== undefined ? { onChainDisputeCount } : {}),
449
- region,
450
- timestamp,
451
- signature,
452
- };
453
- }
@@ -1,7 +0,0 @@
1
- import type { PeerMetadata } from "./peer-metadata.js";
2
-
3
- export type PeerEndpoint = { host: string; port: number };
4
-
5
- export interface MetadataResolver {
6
- resolve(peer: PeerEndpoint): Promise<PeerMetadata | null>;
7
- }
@@ -1,73 +0,0 @@
1
- import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';
2
- import type { PeerMetadata } from './peer-metadata.js';
3
-
4
- export interface MetadataServerConfig {
5
- port: number;
6
- host?: string;
7
- getMetadata: () => PeerMetadata | null;
8
- }
9
-
10
- /** @deprecated Standalone MetadataServer is unused; ConnectionManager._serveHttpMetadata is preferred. */
11
- export class MetadataServer {
12
- private readonly _config: MetadataServerConfig;
13
- private _server: Server | null = null;
14
-
15
- constructor(config: MetadataServerConfig) {
16
- this._config = config;
17
- }
18
-
19
- async start(): Promise<void> {
20
- return new Promise((resolve, reject) => {
21
- this._server = createServer((req: IncomingMessage, res: ServerResponse) => {
22
- if (req.method !== 'GET') {
23
- res.writeHead(405, { 'content-type': 'application/json' });
24
- res.end(JSON.stringify({ error: 'method not allowed' }));
25
- return;
26
- }
27
-
28
- if (req.url !== '/metadata') {
29
- res.writeHead(404, { 'content-type': 'application/json' });
30
- res.end(JSON.stringify({ error: 'not found' }));
31
- return;
32
- }
33
-
34
- const metadata = this._config.getMetadata();
35
- if (!metadata) {
36
- res.writeHead(503, { 'content-type': 'application/json' });
37
- res.end(JSON.stringify({ error: 'metadata not available' }));
38
- return;
39
- }
40
-
41
- res.writeHead(200, { 'content-type': 'application/json' });
42
- res.end(JSON.stringify(metadata));
43
- });
44
-
45
- this._server.on('error', reject);
46
- this._server.listen(this._config.port, this._config.host ?? '0.0.0.0', () => {
47
- resolve();
48
- });
49
- });
50
- }
51
-
52
- async stop(): Promise<void> {
53
- return new Promise((resolve) => {
54
- if (!this._server) {
55
- resolve();
56
- return;
57
- }
58
- this._server.close(() => {
59
- this._server = null;
60
- resolve();
61
- });
62
- });
63
- }
64
-
65
- getPort(): number {
66
- if (!this._server) return this._config.port;
67
- const addr = this._server.address();
68
- if (addr && typeof addr !== 'string') {
69
- return addr.port;
70
- }
71
- return this._config.port;
72
- }
73
- }
@@ -1,172 +0,0 @@
1
- import type { PeerMetadata } from "./peer-metadata.js";
2
- import { METADATA_VERSION } from "./peer-metadata.js";
3
- import { encodeMetadata } from "./metadata-codec.js";
4
-
5
- export const MAX_METADATA_SIZE = 1000;
6
- export const MAX_PROVIDERS = 10;
7
- export const MAX_MODELS_PER_PROVIDER = 20;
8
- export const MAX_MODEL_NAME_LENGTH = 64;
9
- export const MAX_REGION_LENGTH = 32;
10
-
11
- export interface ValidationError {
12
- field: string;
13
- message: string;
14
- }
15
-
16
- export function validateMetadata(metadata: PeerMetadata): ValidationError[] {
17
- const errors: ValidationError[] = [];
18
-
19
- // version
20
- if (metadata.version !== METADATA_VERSION) {
21
- errors.push({
22
- field: "version",
23
- message: `Expected version ${METADATA_VERSION}, got ${metadata.version}`,
24
- });
25
- }
26
-
27
- // peerId length (64 hex chars = 32 bytes)
28
- if (!/^[0-9a-f]{64}$/.test(metadata.peerId)) {
29
- errors.push({
30
- field: "peerId",
31
- message: "PeerId must be exactly 64 lowercase hex characters",
32
- });
33
- }
34
-
35
- // region
36
- if (!metadata.region || metadata.region.length === 0) {
37
- errors.push({
38
- field: "region",
39
- message: "Region must not be empty",
40
- });
41
- } else if (metadata.region.length > MAX_REGION_LENGTH) {
42
- errors.push({
43
- field: "region",
44
- message: `Region length ${metadata.region.length} exceeds max ${MAX_REGION_LENGTH}`,
45
- });
46
- }
47
-
48
- // timestamp
49
- if (metadata.timestamp <= 0 || !Number.isFinite(metadata.timestamp)) {
50
- errors.push({
51
- field: "timestamp",
52
- message: "Timestamp must be a positive finite number",
53
- });
54
- }
55
-
56
- // providers count
57
- if (metadata.providers.length === 0) {
58
- errors.push({
59
- field: "providers",
60
- message: "Must have at least one provider",
61
- });
62
- } else if (metadata.providers.length > MAX_PROVIDERS) {
63
- errors.push({
64
- field: "providers",
65
- message: `Provider count ${metadata.providers.length} exceeds max ${MAX_PROVIDERS}`,
66
- });
67
- }
68
-
69
- // each provider
70
- for (let i = 0; i < metadata.providers.length; i++) {
71
- const p = metadata.providers[i]!;
72
-
73
- // models count
74
- if (p.models.length > MAX_MODELS_PER_PROVIDER) {
75
- errors.push({
76
- field: `providers[${i}].models`,
77
- message: `Model count ${p.models.length} exceeds max ${MAX_MODELS_PER_PROVIDER}`,
78
- });
79
- }
80
-
81
- // model name length
82
- for (let j = 0; j < p.models.length; j++) {
83
- const model = p.models[j]!;
84
- if (model.length > MAX_MODEL_NAME_LENGTH) {
85
- errors.push({
86
- field: `providers[${i}].models[${j}]`,
87
- message: `Model name length ${model.length} exceeds max ${MAX_MODEL_NAME_LENGTH}`,
88
- });
89
- }
90
- }
91
-
92
- // default pricing
93
- if (!Number.isFinite(p.defaultPricing?.inputUsdPerMillion) || p.defaultPricing.inputUsdPerMillion < 0) {
94
- errors.push({
95
- field: `providers[${i}].defaultPricing.inputUsdPerMillion`,
96
- message: "Default input price must be a non-negative finite number",
97
- });
98
- }
99
- if (!Number.isFinite(p.defaultPricing?.outputUsdPerMillion) || p.defaultPricing.outputUsdPerMillion < 0) {
100
- errors.push({
101
- field: `providers[${i}].defaultPricing.outputUsdPerMillion`,
102
- message: "Default output price must be a non-negative finite number",
103
- });
104
- }
105
-
106
- // model pricing (optional)
107
- if (p.modelPricing !== undefined) {
108
- for (const [modelName, modelPricing] of Object.entries(p.modelPricing)) {
109
- if (!modelPricing || !Number.isFinite(modelPricing.inputUsdPerMillion) || modelPricing.inputUsdPerMillion < 0) {
110
- errors.push({
111
- field: `providers[${i}].modelPricing.${modelName}.inputUsdPerMillion`,
112
- message: "Model input price must be a non-negative finite number",
113
- });
114
- }
115
- if (!modelPricing || !Number.isFinite(modelPricing.outputUsdPerMillion) || modelPricing.outputUsdPerMillion < 0) {
116
- errors.push({
117
- field: `providers[${i}].modelPricing.${modelName}.outputUsdPerMillion`,
118
- message: "Model output price must be a non-negative finite number",
119
- });
120
- }
121
- }
122
- }
123
-
124
- // concurrency
125
- if (p.maxConcurrency < 1) {
126
- errors.push({
127
- field: `providers[${i}].maxConcurrency`,
128
- message: "Max concurrency must be at least 1",
129
- });
130
- }
131
-
132
- // currentLoad
133
- if (p.currentLoad < 0) {
134
- errors.push({
135
- field: `providers[${i}].currentLoad`,
136
- message: "Current load must be non-negative",
137
- });
138
- }
139
- if (p.currentLoad > p.maxConcurrency) {
140
- errors.push({
141
- field: `providers[${i}].currentLoad`,
142
- message: "Current load must not exceed max concurrency",
143
- });
144
- }
145
- }
146
-
147
- // signature length (128 hex chars = 64 bytes)
148
- if (!/^[0-9a-f]{128}$/.test(metadata.signature)) {
149
- errors.push({
150
- field: "signature",
151
- message: "Signature must be exactly 128 lowercase hex characters (64 bytes)",
152
- });
153
- }
154
-
155
- // encoded size
156
- try {
157
- const encoded = encodeMetadata(metadata);
158
- if (encoded.length > MAX_METADATA_SIZE) {
159
- errors.push({
160
- field: "encoded",
161
- message: `Encoded size ${encoded.length} exceeds max ${MAX_METADATA_SIZE}`,
162
- });
163
- }
164
- } catch {
165
- errors.push({
166
- field: "encoded",
167
- message: "Failed to encode metadata for size check",
168
- });
169
- }
170
-
171
- return errors;
172
- }