@hivemind-os/collective-core 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.
Files changed (145) hide show
  1. package/.test-data/05a845c4-1682-41b9-97e5-65a5263156c0/spending.sqlite +0 -0
  2. package/.test-data/10e18ba5-98f0-42e0-8899-06a09459ae85/agents.sqlite +0 -0
  3. package/.test-data/20464456-23cb-4ff7-8df5-3c129fb95a90/agents.sqlite +0 -0
  4. package/.test-data/2e2ec66c-e8b4-43eb-945f-cca84ed0a0f6/agents.sqlite +0 -0
  5. package/.test-data/2f5ef9b7-01da-4ba0-a6fa-af8063a2567c/agents.sqlite +0 -0
  6. package/.test-data/3ac13cd5-84c9-4e71-8b89-6d3ef4dd819c/agents.sqlite +0 -0
  7. package/.test-data/40b7dd4a-fae0-4a5d-a6a0-64c9048af35e/agents.sqlite +0 -0
  8. package/.test-data/59511a04-42f8-4286-bb1e-733795e08749/agents.sqlite +0 -0
  9. package/.test-data/6778e8c5-6eb5-416d-8aca-9d559cdf83e4/agents.sqlite +0 -0
  10. package/.test-data/7648dc86-df90-4460-8f9c-55cdb481324b/agents.sqlite +0 -0
  11. package/.test-data/77162d98-6b22-41b1-a50f-536fab739f8a/agents.sqlite +0 -0
  12. package/.test-data/798dbdab-cbe7-4edd-8a5b-ae99c285fe17/agents.sqlite +0 -0
  13. package/.test-data/8033d6ac-8b85-454d-b708-00ac695b22f8/agents.sqlite +0 -0
  14. package/.test-data/9155a4f4-cda3-487c-9eed-921c82d7550f/agents.sqlite +0 -0
  15. package/.test-data/9bfeee53-c231-46c3-8c93-2180933f5d50/agents.sqlite +0 -0
  16. package/.test-data/a4c64287-79f6-46e9-847d-2803c63a74fd/agents.sqlite +0 -0
  17. package/.test-data/b8f58952-1ed8-46ff-abd7-21fb86e9457f/agents.sqlite +0 -0
  18. package/.test-data/c3060504-3187-41ed-8532-82332be48b0b/spending.sqlite +0 -0
  19. package/.test-data/cc471629-8006-4fc1-b8a1-399d2df2cc4e/agents.sqlite +0 -0
  20. package/.test-data/dbca3bef-397d-4bbc-bd4c-4f7b14103e04/spending.sqlite +0 -0
  21. package/.test-data/f1283dd1-6602-4de7-a050-16aac7abc288/agents.sqlite +0 -0
  22. package/.turbo/turbo-build.log +14 -0
  23. package/dist/index.d.ts +1675 -0
  24. package/dist/index.js +8006 -0
  25. package/dist/index.js.map +1 -0
  26. package/package.json +41 -0
  27. package/src/auth/device-flow.ts +108 -0
  28. package/src/auth/ed25519-provider.ts +43 -0
  29. package/src/auth/errors.ts +82 -0
  30. package/src/auth/evm-key.ts +55 -0
  31. package/src/auth/index.ts +8 -0
  32. package/src/auth/session-state.ts +25 -0
  33. package/src/auth/session-store.ts +510 -0
  34. package/src/auth/types.ts +81 -0
  35. package/src/auth/zklogin-provider.ts +902 -0
  36. package/src/blobstore/WALRUS_FINDINGS.md +284 -0
  37. package/src/blobstore/encrypted-store.ts +56 -0
  38. package/src/blobstore/fs-store.ts +91 -0
  39. package/src/blobstore/hybrid-store.ts +144 -0
  40. package/src/blobstore/index.ts +5 -0
  41. package/src/blobstore/interface.ts +33 -0
  42. package/src/blobstore/walrus-spike.ts +345 -0
  43. package/src/blobstore/walrus-store.ts +551 -0
  44. package/src/cache/agent-cache.ts +403 -0
  45. package/src/cache/index.ts +1 -0
  46. package/src/crypto/encryption.ts +152 -0
  47. package/src/crypto/index.ts +2 -0
  48. package/src/crypto/x25519.ts +41 -0
  49. package/src/dispute/client.ts +191 -0
  50. package/src/dispute/index.ts +1 -0
  51. package/src/events/index.ts +2 -0
  52. package/src/events/parser.ts +291 -0
  53. package/src/events/subscription.ts +131 -0
  54. package/src/evm/constants.ts +6 -0
  55. package/src/evm/index.ts +2 -0
  56. package/src/evm/wallet.ts +136 -0
  57. package/src/identity/did.ts +36 -0
  58. package/src/identity/index.ts +4 -0
  59. package/src/identity/keypair.ts +199 -0
  60. package/src/identity/signing.ts +28 -0
  61. package/src/index.ts +22 -0
  62. package/src/internal/parsing.ts +416 -0
  63. package/src/marketplace/client.ts +349 -0
  64. package/src/marketplace/index.ts +1 -0
  65. package/src/metering/hash-chain.ts +94 -0
  66. package/src/metering/index.ts +4 -0
  67. package/src/metering/meter.ts +80 -0
  68. package/src/metering/streaming.ts +196 -0
  69. package/src/metering/verification.ts +104 -0
  70. package/src/payment/index.ts +1 -0
  71. package/src/payment/rail-selector.ts +41 -0
  72. package/src/registry/client.ts +328 -0
  73. package/src/registry/index.ts +1 -0
  74. package/src/relay/consumer-client.ts +497 -0
  75. package/src/relay/index.ts +1 -0
  76. package/src/relay-registry/client.ts +295 -0
  77. package/src/relay-registry/discovery.ts +109 -0
  78. package/src/relay-registry/index.ts +2 -0
  79. package/src/reputation/anchor-client.ts +126 -0
  80. package/src/reputation/event-publisher.ts +67 -0
  81. package/src/reputation/index.ts +5 -0
  82. package/src/reputation/merkle.ts +79 -0
  83. package/src/reputation/score-calculator.ts +133 -0
  84. package/src/reputation/serialization.ts +37 -0
  85. package/src/reputation/store.ts +165 -0
  86. package/src/reputation/validation.ts +135 -0
  87. package/src/routing/circuit-breaker.ts +111 -0
  88. package/src/routing/fan-out.ts +266 -0
  89. package/src/routing/index.ts +4 -0
  90. package/src/routing/performance.ts +244 -0
  91. package/src/routing/selector.ts +225 -0
  92. package/src/spending/index.ts +1 -0
  93. package/src/spending/policy.ts +271 -0
  94. package/src/staking/client.ts +319 -0
  95. package/src/staking/index.ts +1 -0
  96. package/src/sui/client.ts +214 -0
  97. package/src/sui/index.ts +2 -0
  98. package/src/sui/tx-helpers.ts +1070 -0
  99. package/src/task/client.ts +215 -0
  100. package/src/task/index.ts +1 -0
  101. package/src/x402/client.ts +295 -0
  102. package/src/x402/index.ts +1 -0
  103. package/tests/auth/device-flow.test.ts +62 -0
  104. package/tests/auth/ed25519-provider.test.ts +24 -0
  105. package/tests/auth/evm-key.test.ts +31 -0
  106. package/tests/auth/session-store.test.ts +201 -0
  107. package/tests/auth/zklogin-provider.test.ts +366 -0
  108. package/tests/blobstore/encrypted-store.test.ts +78 -0
  109. package/tests/blobstore.test.ts +91 -0
  110. package/tests/cache.test.ts +124 -0
  111. package/tests/crypto/encryption.test.ts +70 -0
  112. package/tests/crypto/x25519.test.ts +47 -0
  113. package/tests/dispute/client.test.ts +238 -0
  114. package/tests/events.test.ts +202 -0
  115. package/tests/evm/wallet.test.ts +101 -0
  116. package/tests/hybrid-store.test.ts +121 -0
  117. package/tests/identity.test.ts +161 -0
  118. package/tests/marketplace.test.ts +308 -0
  119. package/tests/metering/hash-chain.test.ts +32 -0
  120. package/tests/metering/meter.test.ts +23 -0
  121. package/tests/metering/streaming.test.ts +52 -0
  122. package/tests/metering/verification.test.ts +27 -0
  123. package/tests/payment/rail-selector.test.ts +95 -0
  124. package/tests/registry.test.ts +183 -0
  125. package/tests/relay-consumer-client.test.ts +119 -0
  126. package/tests/relay-registry/client.test.ts +261 -0
  127. package/tests/reputation/event-publisher.test.ts +70 -0
  128. package/tests/reputation/merkle.test.ts +44 -0
  129. package/tests/reputation/score-calculator.test.ts +104 -0
  130. package/tests/reputation/store.test.ts +94 -0
  131. package/tests/routing/circuit-breaker.test.ts +45 -0
  132. package/tests/routing/fan-out.test.ts +123 -0
  133. package/tests/routing/performance.test.ts +49 -0
  134. package/tests/routing/selector.test.ts +114 -0
  135. package/tests/spending.test.ts +133 -0
  136. package/tests/staking/client.test.ts +286 -0
  137. package/tests/sui-client.test.ts +85 -0
  138. package/tests/task.test.ts +249 -0
  139. package/tests/tx-helpers.test.ts +70 -0
  140. package/tests/walrus-spike.test.ts +100 -0
  141. package/tests/walrus-store.test.ts +196 -0
  142. package/tests/x402/client.test.ts +116 -0
  143. package/tsconfig.json +9 -0
  144. package/tsup.config.ts +11 -0
  145. package/vitest.config.ts +8 -0
@@ -0,0 +1,345 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { performance } from 'node:perf_hooks';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ export const DEFAULT_WALRUS_PUBLISHER_URL = 'https://publisher.walrus-testnet.walrus.space';
6
+ export const DEFAULT_WALRUS_AGGREGATOR_URL = 'https://aggregator.walrus-testnet.walrus.space';
7
+ export const DEFAULT_WALRUS_EPOCHS = 1;
8
+ export const WALRUS_BLOB_ID_BYTES = 32;
9
+
10
+ const WALRUS_BLOB_ID_PATTERN = /^[A-Za-z0-9_-]{43}$/;
11
+
12
+ export interface WalrusEndpoints {
13
+ publisherUrl?: string;
14
+ aggregatorUrl?: string;
15
+ fetchImpl?: typeof fetch;
16
+ }
17
+
18
+ export interface WalrusStoreOptions extends WalrusEndpoints {
19
+ epochs?: number;
20
+ permanent?: boolean;
21
+ deletable?: boolean;
22
+ contentType?: string;
23
+ }
24
+
25
+ export interface WalrusBlobMetadata {
26
+ blobId: string;
27
+ objectId?: string;
28
+ size?: number;
29
+ deletable?: boolean;
30
+ endEpoch?: number;
31
+ rawResponse: WalrusStoreApiResponse;
32
+ }
33
+
34
+ export interface WalrusStoreResult extends WalrusBlobMetadata {
35
+ checksum: string;
36
+ storeMs: number;
37
+ }
38
+
39
+ export interface WalrusRoundTripResult extends WalrusStoreResult {
40
+ blobIdBytes: Uint8Array;
41
+ fetchedChecksum: string;
42
+ fetchMs: number;
43
+ fetchedBytes: Uint8Array;
44
+ contentMatches: boolean;
45
+ }
46
+
47
+ export interface WalrusSpikeOptions extends WalrusStoreOptions {
48
+ data?: Uint8Array;
49
+ }
50
+
51
+ interface WalrusBlobObject {
52
+ id: string;
53
+ blobId: string;
54
+ size: number;
55
+ deletable: boolean;
56
+ storage?: {
57
+ endEpoch?: number;
58
+ };
59
+ }
60
+
61
+ interface WalrusNewlyCreatedResponse {
62
+ newlyCreated: {
63
+ blobObject: WalrusBlobObject;
64
+ };
65
+ }
66
+
67
+ interface WalrusAlreadyCertifiedResponse {
68
+ alreadyCertified: {
69
+ blobId: string;
70
+ endEpoch?: number;
71
+ };
72
+ }
73
+
74
+ export type WalrusStoreApiResponse = WalrusNewlyCreatedResponse | WalrusAlreadyCertifiedResponse;
75
+
76
+ export class WalrusHttpError extends Error {
77
+ readonly status: number;
78
+ readonly url: string;
79
+ readonly body: string;
80
+
81
+ constructor(message: string, status: number, url: string, body: string) {
82
+ super(message);
83
+ this.name = 'WalrusHttpError';
84
+ this.status = status;
85
+ this.url = url;
86
+ this.body = body;
87
+ }
88
+ }
89
+
90
+ export class WalrusNetworkError extends Error {
91
+ readonly url: string;
92
+
93
+ constructor(message: string, url: string, options?: ErrorOptions) {
94
+ super(message, options);
95
+ this.name = 'WalrusNetworkError';
96
+ this.url = url;
97
+ }
98
+ }
99
+
100
+ export function computeWalrusChecksum(data: Uint8Array): string {
101
+ return createHash('sha256').update(data).digest('hex');
102
+ }
103
+
104
+ export function decodeWalrusBlobId(blobId: string): Uint8Array {
105
+ if (!WALRUS_BLOB_ID_PATTERN.test(blobId)) {
106
+ throw new Error(`Invalid Walrus blob ID: ${blobId}`);
107
+ }
108
+
109
+ const bytes = Buffer.from(blobId, 'base64url');
110
+ if (bytes.length !== WALRUS_BLOB_ID_BYTES) {
111
+ throw new Error(
112
+ `Expected Walrus blob IDs to decode to ${WALRUS_BLOB_ID_BYTES} bytes, got ${bytes.length}`,
113
+ );
114
+ }
115
+
116
+ return new Uint8Array(bytes);
117
+ }
118
+
119
+ export function walrusBlobIdToMoveVector(blobId: string): Uint8Array {
120
+ return decodeWalrusBlobId(blobId);
121
+ }
122
+
123
+ export function moveVectorToWalrusBlobId(bytes: Uint8Array): string {
124
+ if (bytes.length !== WALRUS_BLOB_ID_BYTES) {
125
+ throw new Error(
126
+ `Expected ${WALRUS_BLOB_ID_BYTES} bytes when converting Move blob IDs, got ${bytes.length}`,
127
+ );
128
+ }
129
+
130
+ return Buffer.from(bytes).toString('base64url');
131
+ }
132
+
133
+ export async function storeBlobOnWalrus(
134
+ data: Uint8Array,
135
+ options: WalrusStoreOptions = {},
136
+ ): Promise<WalrusStoreResult> {
137
+ if (options.permanent && options.deletable) {
138
+ throw new Error('Walrus blobs cannot be both permanent and deletable.');
139
+ }
140
+
141
+ const url = new URL(`${normalizeWalrusUrl(options.publisherUrl ?? DEFAULT_WALRUS_PUBLISHER_URL)}/v1/blobs`);
142
+ const epochs = options.epochs ?? DEFAULT_WALRUS_EPOCHS;
143
+ validateEpochs(epochs);
144
+ url.searchParams.set('epochs', String(epochs));
145
+
146
+ if (options.permanent) {
147
+ url.searchParams.set('permanent', 'true');
148
+ }
149
+
150
+ if (options.deletable) {
151
+ url.searchParams.set('deletable', 'true');
152
+ }
153
+
154
+ const requestStart = performance.now();
155
+ const response = await requestWalrus(url.toString(), {
156
+ method: 'PUT',
157
+ headers: {
158
+ 'content-type': options.contentType ?? 'application/octet-stream',
159
+ },
160
+ body: data,
161
+ }, options.fetchImpl);
162
+ const storeMs = Math.round(performance.now() - requestStart);
163
+ const body = await response.text();
164
+
165
+ if (!response.ok) {
166
+ throw new WalrusHttpError(
167
+ `Walrus store failed with status ${response.status}: ${body}`,
168
+ response.status,
169
+ url.toString(),
170
+ body,
171
+ );
172
+ }
173
+
174
+ const rawResponse = JSON.parse(body) as WalrusStoreApiResponse;
175
+ const metadata = parseWalrusStoreResponse(rawResponse);
176
+
177
+ return {
178
+ ...metadata,
179
+ checksum: computeWalrusChecksum(data),
180
+ storeMs,
181
+ };
182
+ }
183
+
184
+ export async function fetchBlobFromWalrus(
185
+ blobId: string,
186
+ options: WalrusEndpoints = {},
187
+ ): Promise<Uint8Array | null> {
188
+ decodeWalrusBlobId(blobId);
189
+
190
+ const url = `${normalizeWalrusUrl(options.aggregatorUrl ?? DEFAULT_WALRUS_AGGREGATOR_URL)}/v1/blobs/${blobId}`;
191
+ const response = await requestWalrus(url, { method: 'GET' }, options.fetchImpl);
192
+
193
+ if (response.status === 404) {
194
+ return null;
195
+ }
196
+
197
+ if (!response.ok) {
198
+ const body = await response.text();
199
+ throw new WalrusHttpError(
200
+ `Walrus fetch failed with status ${response.status}: ${body}`,
201
+ response.status,
202
+ url,
203
+ body,
204
+ );
205
+ }
206
+
207
+ return new Uint8Array(await response.arrayBuffer());
208
+ }
209
+
210
+ export async function walrusBlobExists(
211
+ blobId: string,
212
+ options: WalrusEndpoints = {},
213
+ ): Promise<boolean> {
214
+ decodeWalrusBlobId(blobId);
215
+
216
+ const url = `${normalizeWalrusUrl(options.aggregatorUrl ?? DEFAULT_WALRUS_AGGREGATOR_URL)}/v1/blobs/${blobId}`;
217
+ const response = await requestWalrus(
218
+ url,
219
+ {
220
+ method: 'GET',
221
+ headers: { Range: 'bytes=0-0' },
222
+ },
223
+ options.fetchImpl,
224
+ );
225
+
226
+ if (response.status === 404) {
227
+ return false;
228
+ }
229
+
230
+ if (response.status === 200 || response.status === 206) {
231
+ return true;
232
+ }
233
+
234
+ const body = await response.text();
235
+ throw new WalrusHttpError(
236
+ `Walrus exists check failed with status ${response.status}: ${body}`,
237
+ response.status,
238
+ url,
239
+ body,
240
+ );
241
+ }
242
+
243
+ export async function runWalrusSpike(
244
+ options: WalrusSpikeOptions = {},
245
+ ): Promise<WalrusRoundTripResult> {
246
+ const data = options.data ?? new TextEncoder().encode('Hello, Walrus!');
247
+ const stored = await storeBlobOnWalrus(data, options);
248
+ const fetchStart = performance.now();
249
+ const fetchedBytes = await fetchBlobFromWalrus(stored.blobId, options);
250
+ const fetchMs = Math.round(performance.now() - fetchStart);
251
+
252
+ if (!fetchedBytes) {
253
+ throw new Error(`Walrus blob ${stored.blobId} was stored but could not be fetched.`);
254
+ }
255
+
256
+ const fetchedChecksum = computeWalrusChecksum(fetchedBytes);
257
+
258
+ return {
259
+ ...stored,
260
+ blobIdBytes: walrusBlobIdToMoveVector(stored.blobId),
261
+ fetchedChecksum,
262
+ fetchMs,
263
+ fetchedBytes,
264
+ contentMatches: fetchedChecksum === stored.checksum,
265
+ };
266
+ }
267
+
268
+ function normalizeWalrusUrl(url: string): string {
269
+ return url.replace(/\/+$/, '');
270
+ }
271
+
272
+ function validateEpochs(epochs: number): void {
273
+ if (!Number.isInteger(epochs) || epochs < 1) {
274
+ throw new Error(`Walrus epochs must be a positive integer, received ${epochs}`);
275
+ }
276
+ }
277
+
278
+ function parseWalrusStoreResponse(response: WalrusStoreApiResponse): WalrusBlobMetadata {
279
+ if ('newlyCreated' in response) {
280
+ const { blobObject } = response.newlyCreated;
281
+ decodeWalrusBlobId(blobObject.blobId);
282
+ return {
283
+ blobId: blobObject.blobId,
284
+ objectId: blobObject.id,
285
+ size: blobObject.size,
286
+ deletable: blobObject.deletable,
287
+ endEpoch: blobObject.storage?.endEpoch,
288
+ rawResponse: response,
289
+ };
290
+ }
291
+
292
+ if ('alreadyCertified' in response) {
293
+ decodeWalrusBlobId(response.alreadyCertified.blobId);
294
+ return {
295
+ blobId: response.alreadyCertified.blobId,
296
+ endEpoch: response.alreadyCertified.endEpoch,
297
+ rawResponse: response,
298
+ };
299
+ }
300
+
301
+ throw new Error('Unexpected Walrus store response payload.');
302
+ }
303
+
304
+ async function requestWalrus(
305
+ url: string,
306
+ init: RequestInit,
307
+ fetchImpl: typeof fetch | undefined,
308
+ ): Promise<Response> {
309
+ const walrusFetch = fetchImpl ?? globalThis.fetch;
310
+ if (!walrusFetch) {
311
+ throw new Error('Global fetch is not available in this runtime.');
312
+ }
313
+
314
+ try {
315
+ return await walrusFetch(url, init);
316
+ } catch (error) {
317
+ throw new WalrusNetworkError(
318
+ `Walrus request failed for ${url}: ${(error as Error).message}`,
319
+ url,
320
+ { cause: error as Error },
321
+ );
322
+ }
323
+ }
324
+
325
+ async function main(): Promise<void> {
326
+ const result = await runWalrusSpike({ permanent: true });
327
+ console.log(
328
+ JSON.stringify(
329
+ {
330
+ ...result,
331
+ blobIdBytes: Array.from(result.blobIdBytes),
332
+ fetchedBytes: Array.from(result.fetchedBytes),
333
+ },
334
+ null,
335
+ 2,
336
+ ),
337
+ );
338
+ }
339
+
340
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
341
+ void main().catch((error) => {
342
+ console.error(error);
343
+ process.exitCode = 1;
344
+ });
345
+ }