@elisym/sdk 0.1.2

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.cjs ADDED
@@ -0,0 +1,1199 @@
1
+ 'use strict';
2
+
3
+ var nostrTools = require('nostr-tools');
4
+ var nip44 = require('nostr-tools/nip44');
5
+ var nip17 = require('nostr-tools/nip17');
6
+ var nip59 = require('nostr-tools/nip59');
7
+ var web3_js = require('@solana/web3.js');
8
+ var Decimal = require('decimal.js-light');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var nip44__namespace = /*#__PURE__*/_interopNamespace(nip44);
31
+ var nip17__namespace = /*#__PURE__*/_interopNamespace(nip17);
32
+ var nip59__namespace = /*#__PURE__*/_interopNamespace(nip59);
33
+ var Decimal__default = /*#__PURE__*/_interopDefault(Decimal);
34
+
35
+ // src/core/pool.ts
36
+
37
+ // src/constants.ts
38
+ var RELAYS = [
39
+ "wss://relay.damus.io",
40
+ "wss://nos.lol",
41
+ "wss://relay.nostr.band",
42
+ "wss://relay.primal.net",
43
+ "wss://relay.snort.social"
44
+ ];
45
+ var KIND_APP_HANDLER = 31990;
46
+ var KIND_JOB_REQUEST_BASE = 5e3;
47
+ var KIND_JOB_RESULT_BASE = 6e3;
48
+ var KIND_JOB_FEEDBACK = 7e3;
49
+ var DEFAULT_KIND_OFFSET = 100;
50
+ var KIND_GIFT_WRAP = 1059;
51
+ var KIND_JOB_REQUEST = KIND_JOB_REQUEST_BASE + DEFAULT_KIND_OFFSET;
52
+ var KIND_JOB_RESULT = KIND_JOB_RESULT_BASE + DEFAULT_KIND_OFFSET;
53
+ function jobRequestKind(offset) {
54
+ return KIND_JOB_REQUEST_BASE + offset;
55
+ }
56
+ function jobResultKind(offset) {
57
+ return KIND_JOB_RESULT_BASE + offset;
58
+ }
59
+ var KIND_PING = 20200;
60
+ var KIND_PONG = 20201;
61
+ var LAMPORTS_PER_SOL = 1e9;
62
+ var PROTOCOL_FEE_BPS = 300;
63
+ var PROTOCOL_TREASURY = "GY7vnWMkKpftU4nQ16C2ATkj1JwrQpHhknkaBUn67VTy";
64
+
65
+ // src/core/pool.ts
66
+ var NostrPool = class {
67
+ pool;
68
+ relays;
69
+ constructor(relays = RELAYS) {
70
+ this.pool = new nostrTools.SimplePool();
71
+ this.relays = relays;
72
+ }
73
+ async querySync(filter) {
74
+ return Promise.race([
75
+ this.pool.querySync(this.relays, filter),
76
+ new Promise(
77
+ (resolve) => setTimeout(() => resolve([]), 15e3)
78
+ )
79
+ ]);
80
+ }
81
+ async queryBatched(filter, keys, batchSize = 250) {
82
+ const batches = [];
83
+ for (let i = 0; i < keys.length; i += batchSize) {
84
+ const batch = keys.slice(i, i + batchSize);
85
+ batches.push(
86
+ Promise.race([
87
+ this.pool.querySync(this.relays, {
88
+ ...filter,
89
+ authors: batch
90
+ }),
91
+ new Promise(
92
+ (resolve) => setTimeout(() => resolve([]), 15e3)
93
+ )
94
+ ])
95
+ );
96
+ }
97
+ return (await Promise.all(batches)).flat();
98
+ }
99
+ async queryBatchedByTag(filter, tagName, values, batchSize = 250) {
100
+ const batches = [];
101
+ for (let i = 0; i < values.length; i += batchSize) {
102
+ const batch = values.slice(i, i + batchSize);
103
+ batches.push(
104
+ Promise.race([
105
+ this.pool.querySync(this.relays, {
106
+ ...filter,
107
+ [`#${tagName}`]: batch
108
+ }),
109
+ new Promise(
110
+ (resolve) => setTimeout(() => resolve([]), 15e3)
111
+ )
112
+ ])
113
+ );
114
+ }
115
+ return (await Promise.all(batches)).flat();
116
+ }
117
+ async publish(event) {
118
+ try {
119
+ await Promise.any(this.pool.publish(this.relays, event));
120
+ } catch (err) {
121
+ if (err instanceof AggregateError) {
122
+ throw new Error(
123
+ `Failed to publish to all ${this.relays.length} relays: ${err.errors.map((e) => e.message).join(", ")}`
124
+ );
125
+ }
126
+ throw err;
127
+ }
128
+ }
129
+ /** Publish to all relays and wait for all to settle. Throws if none accepted. */
130
+ async publishAll(event) {
131
+ const results = await Promise.allSettled(this.pool.publish(this.relays, event));
132
+ const anyOk = results.some((r) => r.status === "fulfilled");
133
+ if (!anyOk) {
134
+ throw new Error(
135
+ `Failed to publish to all ${this.relays.length} relays`
136
+ );
137
+ }
138
+ }
139
+ subscribe(filter, onEvent) {
140
+ return this.pool.subscribeMany(
141
+ this.relays,
142
+ filter,
143
+ { onevent: onEvent }
144
+ );
145
+ }
146
+ /**
147
+ * Subscribe and wait until at least one relay confirms the subscription
148
+ * is active (EOSE). Resolves on the first relay that responds.
149
+ * Essential for ephemeral events where the subscription must be live
150
+ * before publishing.
151
+ */
152
+ subscribeAndWait(filter, onEvent, timeoutMs = 3e3) {
153
+ return new Promise((resolve) => {
154
+ let resolved = false;
155
+ const done = () => {
156
+ if (resolved) return;
157
+ resolved = true;
158
+ resolve(combinedSub);
159
+ };
160
+ const subs = [];
161
+ for (const relay of this.relays) {
162
+ const sub = this.pool.subscribeMany(
163
+ [relay],
164
+ filter,
165
+ {
166
+ onevent: onEvent,
167
+ oneose: done
168
+ }
169
+ );
170
+ subs.push(sub);
171
+ }
172
+ const combinedSub = {
173
+ close: (reason) => {
174
+ for (const s of subs) s.close(reason);
175
+ }
176
+ };
177
+ setTimeout(done, timeoutMs);
178
+ });
179
+ }
180
+ /**
181
+ * Tear down pool and create a fresh one.
182
+ * Works around nostr-tools `onerror → skipReconnection = true` bug
183
+ * that permanently kills subscriptions. Callers must re-subscribe.
184
+ */
185
+ reset() {
186
+ try {
187
+ this.pool.close(this.relays);
188
+ } catch {
189
+ }
190
+ this.pool = new nostrTools.SimplePool();
191
+ }
192
+ /**
193
+ * Lightweight connectivity probe. Returns true if at least one relay responds.
194
+ */
195
+ async probe(timeoutMs = 3e3) {
196
+ let timer;
197
+ try {
198
+ await Promise.race([
199
+ this.pool.querySync(this.relays, { kinds: [0], limit: 1 }),
200
+ new Promise((_, reject) => {
201
+ timer = setTimeout(() => reject(new Error("probe timeout")), timeoutMs);
202
+ })
203
+ ]);
204
+ return true;
205
+ } catch {
206
+ return false;
207
+ } finally {
208
+ clearTimeout(timer);
209
+ }
210
+ }
211
+ getRelays() {
212
+ return this.relays;
213
+ }
214
+ close() {
215
+ this.pool.close(this.relays);
216
+ }
217
+ };
218
+ var ElisymIdentity = class _ElisymIdentity {
219
+ secretKey;
220
+ publicKey;
221
+ npub;
222
+ constructor(secretKey) {
223
+ this.secretKey = secretKey;
224
+ this.publicKey = nostrTools.getPublicKey(secretKey);
225
+ this.npub = nostrTools.nip19.npubEncode(this.publicKey);
226
+ }
227
+ static generate() {
228
+ return new _ElisymIdentity(nostrTools.generateSecretKey());
229
+ }
230
+ static fromSecretKey(sk) {
231
+ return new _ElisymIdentity(sk);
232
+ }
233
+ static fromHex(hex) {
234
+ if (hex.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(hex)) {
235
+ throw new Error("Invalid secret key hex: expected 64 hex characters (32 bytes).");
236
+ }
237
+ const bytes = new Uint8Array(32);
238
+ for (let i = 0; i < 64; i += 2) {
239
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
240
+ }
241
+ return new _ElisymIdentity(bytes);
242
+ }
243
+ };
244
+ function toDTag(name) {
245
+ return name.toLowerCase().replace(/\s+/g, "-");
246
+ }
247
+ function buildAgentsFromEvents(events, network) {
248
+ const latestByDTag = /* @__PURE__ */ new Map();
249
+ for (const event of events) {
250
+ const dTag = event.tags.find((t) => t[0] === "d")?.[1] ?? "";
251
+ const key = `${event.pubkey}:${dTag}`;
252
+ const prev = latestByDTag.get(key);
253
+ if (!prev || event.created_at > prev.created_at) {
254
+ latestByDTag.set(key, event);
255
+ }
256
+ }
257
+ const accumMap = /* @__PURE__ */ new Map();
258
+ for (const event of latestByDTag.values()) {
259
+ try {
260
+ const card = JSON.parse(event.content);
261
+ if (!card.name || card.deleted) continue;
262
+ const agentNetwork = card.payment?.network ?? "devnet";
263
+ if (agentNetwork !== network) continue;
264
+ const kTags = event.tags.filter((t) => t[0] === "k").map((t) => parseInt(t[1] ?? "", 10)).filter((k) => !isNaN(k));
265
+ const entry = { card, kTags, createdAt: event.created_at };
266
+ const existing = accumMap.get(event.pubkey);
267
+ if (existing) {
268
+ const dupIndex = existing.entries.findIndex((e) => e.card.name === card.name);
269
+ if (dupIndex >= 0) {
270
+ existing.entries[dupIndex] = entry;
271
+ } else {
272
+ existing.entries.push(entry);
273
+ }
274
+ if (event.created_at > existing.lastSeen) {
275
+ existing.lastSeen = event.created_at;
276
+ existing.eventId = event.id;
277
+ }
278
+ } else {
279
+ accumMap.set(event.pubkey, {
280
+ pubkey: event.pubkey,
281
+ npub: nostrTools.nip19.npubEncode(event.pubkey),
282
+ entries: [entry],
283
+ eventId: event.id,
284
+ lastSeen: event.created_at
285
+ });
286
+ }
287
+ } catch {
288
+ }
289
+ }
290
+ const agentMap = /* @__PURE__ */ new Map();
291
+ for (const [pubkey, acc] of accumMap) {
292
+ const supportedKinds = [];
293
+ for (const e of acc.entries) {
294
+ for (const k of e.kTags) {
295
+ if (!supportedKinds.includes(k)) supportedKinds.push(k);
296
+ }
297
+ }
298
+ agentMap.set(pubkey, {
299
+ pubkey: acc.pubkey,
300
+ npub: acc.npub,
301
+ cards: acc.entries.map((e) => e.card),
302
+ eventId: acc.eventId,
303
+ supportedKinds,
304
+ lastSeen: acc.lastSeen
305
+ });
306
+ }
307
+ return agentMap;
308
+ }
309
+ var DiscoveryService = class {
310
+ constructor(pool) {
311
+ this.pool = pool;
312
+ }
313
+ // Instance-level set — avoids module-level state leak across clients
314
+ allSeenAgents = /* @__PURE__ */ new Set();
315
+ /** Count elisym agents (kind:31990 with "elisym" tag). */
316
+ async fetchAllAgentCount() {
317
+ const events = await this.pool.querySync({
318
+ kinds: [KIND_APP_HANDLER],
319
+ "#t": ["elisym"]
320
+ });
321
+ for (const event of events) {
322
+ this.allSeenAgents.add(event.pubkey);
323
+ }
324
+ return this.allSeenAgents.size;
325
+ }
326
+ /**
327
+ * Fetch a single page of elisym agents with relay-side pagination.
328
+ * Uses `until` cursor for Nostr cursor-based pagination.
329
+ * Does NOT fetch activity (faster than fetchAgents).
330
+ */
331
+ async fetchAgentsPage(network = "devnet", limit = 20, until) {
332
+ const filter = {
333
+ kinds: [KIND_APP_HANDLER],
334
+ "#t": ["elisym"],
335
+ limit
336
+ };
337
+ if (until !== void 0) {
338
+ filter.until = until;
339
+ }
340
+ const events = await this.pool.querySync(filter);
341
+ const rawEventCount = events.length;
342
+ let oldestCreatedAt = null;
343
+ for (const event of events) {
344
+ if (oldestCreatedAt === null || event.created_at < oldestCreatedAt) {
345
+ oldestCreatedAt = event.created_at;
346
+ }
347
+ }
348
+ const agentMap = buildAgentsFromEvents(events, network);
349
+ const agents = Array.from(agentMap.values()).sort(
350
+ (a, b) => b.lastSeen - a.lastSeen
351
+ );
352
+ return { agents, oldestCreatedAt, rawEventCount };
353
+ }
354
+ /** Enrich agents with kind:0 metadata (name, picture, about). Mutates in place and returns the same array. */
355
+ async enrichWithMetadata(agents) {
356
+ const pubkeys = agents.map((a) => a.pubkey);
357
+ if (pubkeys.length === 0) return agents;
358
+ const metaEvents = await this.pool.queryBatched(
359
+ { kinds: [0] },
360
+ pubkeys
361
+ );
362
+ const latestMeta = /* @__PURE__ */ new Map();
363
+ for (const ev of metaEvents) {
364
+ const prev = latestMeta.get(ev.pubkey);
365
+ if (!prev || ev.created_at > prev.created_at) {
366
+ latestMeta.set(ev.pubkey, ev);
367
+ }
368
+ }
369
+ const agentLookup = new Map(agents.map((a) => [a.pubkey, a]));
370
+ for (const [pubkey, ev] of latestMeta) {
371
+ const agent = agentLookup.get(pubkey);
372
+ if (!agent) continue;
373
+ try {
374
+ const meta = JSON.parse(ev.content);
375
+ if (meta.picture) agent.picture = meta.picture;
376
+ if (meta.name) agent.name = meta.name;
377
+ if (meta.about) agent.about = meta.about;
378
+ } catch {
379
+ }
380
+ }
381
+ return agents;
382
+ }
383
+ /** Fetch elisym agents filtered by network. */
384
+ async fetchAgents(network = "devnet", limit) {
385
+ const filter = {
386
+ kinds: [KIND_APP_HANDLER],
387
+ "#t": ["elisym"]
388
+ };
389
+ if (limit !== void 0) filter.limit = limit;
390
+ const events = await this.pool.querySync(filter);
391
+ const agentMap = buildAgentsFromEvents(events, network);
392
+ const agentPubkeys = Array.from(agentMap.keys());
393
+ if (agentPubkeys.length > 0) {
394
+ const activitySince = Math.floor(Date.now() / 1e3) - 24 * 60 * 60;
395
+ const resultKinds = /* @__PURE__ */ new Set();
396
+ for (const agent of agentMap.values()) {
397
+ for (const k of agent.supportedKinds) {
398
+ if (k >= KIND_JOB_REQUEST_BASE && k < KIND_JOB_RESULT_BASE) {
399
+ resultKinds.add(KIND_JOB_RESULT_BASE + (k - KIND_JOB_REQUEST_BASE));
400
+ }
401
+ }
402
+ }
403
+ resultKinds.add(jobResultKind(DEFAULT_KIND_OFFSET));
404
+ const activityEvents = await this.pool.queryBatched(
405
+ {
406
+ kinds: [...resultKinds, KIND_JOB_FEEDBACK],
407
+ since: activitySince
408
+ },
409
+ agentPubkeys
410
+ );
411
+ for (const ev of activityEvents) {
412
+ const agent = agentMap.get(ev.pubkey);
413
+ if (agent && ev.created_at > agent.lastSeen) {
414
+ agent.lastSeen = ev.created_at;
415
+ }
416
+ }
417
+ }
418
+ const agents = Array.from(agentMap.values()).sort(
419
+ (a, b) => b.lastSeen - a.lastSeen
420
+ );
421
+ await this.enrichWithMetadata(agents);
422
+ return agents;
423
+ }
424
+ /** Publish a capability card (kind:31990) as a provider. */
425
+ async publishCapability(identity, card, kinds = [KIND_JOB_REQUEST]) {
426
+ if (!card.payment?.address) {
427
+ throw new Error(
428
+ "Cannot publish capability without a payment address. Connect a wallet before publishing."
429
+ );
430
+ }
431
+ const tags = [
432
+ ["d", toDTag(card.name)],
433
+ ["t", "elisym"],
434
+ ...card.capabilities.map((c) => ["t", c]),
435
+ ...kinds.map((k) => ["k", String(k)])
436
+ ];
437
+ const event = nostrTools.finalizeEvent(
438
+ {
439
+ kind: KIND_APP_HANDLER,
440
+ created_at: Math.floor(Date.now() / 1e3),
441
+ tags,
442
+ content: JSON.stringify(card)
443
+ },
444
+ identity.secretKey
445
+ );
446
+ await this.pool.publish(event);
447
+ return event.id;
448
+ }
449
+ /** Publish a Nostr profile (kind:0) as a provider. */
450
+ async publishProfile(identity, name, about, picture) {
451
+ const content = { name, about };
452
+ if (picture) content.picture = picture;
453
+ const event = nostrTools.finalizeEvent(
454
+ {
455
+ kind: 0,
456
+ created_at: Math.floor(Date.now() / 1e3),
457
+ tags: [],
458
+ content: JSON.stringify(content)
459
+ },
460
+ identity.secretKey
461
+ );
462
+ await this.pool.publish(event);
463
+ return event.id;
464
+ }
465
+ /**
466
+ * Delete a capability by publishing a tombstone replacement.
467
+ * Since kind:31990 is a parameterized replaceable event,
468
+ * publishing a new event with the same `d` tag and `"deleted":true`
469
+ * content replaces the old one on all relays.
470
+ */
471
+ async deleteCapability(identity, capabilityName) {
472
+ const dTag = toDTag(capabilityName);
473
+ const event = nostrTools.finalizeEvent(
474
+ {
475
+ kind: KIND_APP_HANDLER,
476
+ created_at: Math.floor(Date.now() / 1e3),
477
+ tags: [
478
+ ["d", dTag],
479
+ ["t", "elisym"]
480
+ ],
481
+ content: JSON.stringify({ deleted: true })
482
+ },
483
+ identity.secretKey
484
+ );
485
+ await this.pool.publishAll(event);
486
+ return event.id;
487
+ }
488
+ };
489
+ function isEncrypted(event) {
490
+ return event.tags.some((t) => t[0] === "encrypted" && t[1] === "nip44");
491
+ }
492
+ function nip44Encrypt(plaintext, secretKey, recipientPubkey) {
493
+ const conversationKey = nip44__namespace.v2.utils.getConversationKey(secretKey, recipientPubkey);
494
+ return nip44__namespace.v2.encrypt(plaintext, conversationKey);
495
+ }
496
+ function nip44Decrypt(ciphertext, secretKey, senderPubkey) {
497
+ const conversationKey = nip44__namespace.v2.utils.getConversationKey(secretKey, senderPubkey);
498
+ return nip44__namespace.v2.decrypt(ciphertext, conversationKey);
499
+ }
500
+ function resolveRequestId(event) {
501
+ return event.tags.find((t) => t[0] === "e")?.[1];
502
+ }
503
+ var MarketplaceService = class {
504
+ constructor(pool) {
505
+ this.pool = pool;
506
+ }
507
+ /** Submit a job request with NIP-44 encrypted input. Returns the event ID. */
508
+ async submitJobRequest(identity, options) {
509
+ if (!options.input) {
510
+ throw new Error("Job input must not be empty.");
511
+ }
512
+ const plaintext = options.input;
513
+ const encrypted = options.providerPubkey ? nip44Encrypt(plaintext, identity.secretKey, options.providerPubkey) : plaintext;
514
+ const iValue = options.providerPubkey ? "encrypted" : "";
515
+ const tags = [
516
+ ["i", iValue, "text"],
517
+ ["t", options.capability],
518
+ ["t", "elisym"],
519
+ ["output", "text/plain"]
520
+ ];
521
+ if (options.providerPubkey) {
522
+ tags.push(["p", options.providerPubkey]);
523
+ tags.push(["encrypted", "nip44"]);
524
+ }
525
+ const kind = jobRequestKind(options.kindOffset ?? DEFAULT_KIND_OFFSET);
526
+ const event = nostrTools.finalizeEvent(
527
+ {
528
+ kind,
529
+ created_at: Math.floor(Date.now() / 1e3),
530
+ tags,
531
+ content: encrypted
532
+ },
533
+ identity.secretKey
534
+ );
535
+ await this.pool.publish(event);
536
+ return event.id;
537
+ }
538
+ /**
539
+ * Subscribe to job updates (feedback + results) for a given job.
540
+ * Returns a cleanup function.
541
+ */
542
+ subscribeToJobUpdates(jobEventId, providerPubkey, customerPublicKey, callbacks, timeoutMs = 12e4, customerSecretKey, kindOffsets) {
543
+ const offsets = kindOffsets ?? [DEFAULT_KIND_OFFSET];
544
+ const resultKinds = offsets.map(jobResultKind);
545
+ const since = Math.floor(Date.now() / 1e3) - 5;
546
+ const subs = [];
547
+ let resolved = false;
548
+ let resultDelivered = false;
549
+ let timer;
550
+ const done = () => {
551
+ resolved = true;
552
+ if (timer) clearTimeout(timer);
553
+ for (const s of subs) s.close();
554
+ };
555
+ const decryptResult = (ev) => {
556
+ if (customerSecretKey && isEncrypted(ev)) {
557
+ try {
558
+ return nip44Decrypt(ev.content, customerSecretKey, ev.pubkey);
559
+ } catch {
560
+ return ev.content;
561
+ }
562
+ }
563
+ return ev.content;
564
+ };
565
+ const handleResult = (ev) => {
566
+ if (resolved || resultDelivered) return;
567
+ if (providerPubkey && ev.pubkey !== providerPubkey) return;
568
+ resultDelivered = true;
569
+ callbacks.onResult?.(decryptResult(ev), ev.id);
570
+ done();
571
+ };
572
+ const feedbackSub = this.pool.subscribe(
573
+ {
574
+ kinds: [KIND_JOB_FEEDBACK],
575
+ "#e": [jobEventId],
576
+ since
577
+ },
578
+ (ev) => {
579
+ if (resolved) return;
580
+ if (providerPubkey && ev.pubkey !== providerPubkey) return;
581
+ const statusTag = ev.tags.find((t) => t[0] === "status");
582
+ if (statusTag?.[1] === "payment-required") {
583
+ const amtTag = ev.tags.find((t) => t[0] === "amount");
584
+ const amt = amtTag?.[1] ? parseInt(amtTag[1], 10) : 0;
585
+ const paymentReq = amtTag?.[2];
586
+ callbacks.onFeedback?.("payment-required", amt, paymentReq);
587
+ }
588
+ }
589
+ );
590
+ subs.push(feedbackSub);
591
+ const resultSub = this.pool.subscribe(
592
+ {
593
+ kinds: resultKinds,
594
+ "#e": [jobEventId],
595
+ since
596
+ },
597
+ handleResult
598
+ );
599
+ subs.push(resultSub);
600
+ const resultSub2 = this.pool.subscribe(
601
+ {
602
+ kinds: resultKinds,
603
+ "#p": [customerPublicKey],
604
+ "#e": [jobEventId],
605
+ since
606
+ },
607
+ handleResult
608
+ );
609
+ subs.push(resultSub2);
610
+ timer = setTimeout(() => {
611
+ if (!resolved) {
612
+ done();
613
+ callbacks.onError?.(`Timed out waiting for response (${timeoutMs / 1e3}s).`);
614
+ }
615
+ }, timeoutMs);
616
+ return done;
617
+ }
618
+ /** Submit payment confirmation feedback. */
619
+ async submitPaymentConfirmation(identity, jobEventId, providerPubkey, txSignature) {
620
+ const event = nostrTools.finalizeEvent(
621
+ {
622
+ kind: KIND_JOB_FEEDBACK,
623
+ created_at: Math.floor(Date.now() / 1e3),
624
+ tags: [
625
+ ["e", jobEventId],
626
+ ["p", providerPubkey],
627
+ ["status", "payment-completed"],
628
+ ["tx", txSignature, "solana"],
629
+ ["t", "elisym"]
630
+ ],
631
+ content: ""
632
+ },
633
+ identity.secretKey
634
+ );
635
+ await this.pool.publish(event);
636
+ }
637
+ /** Submit rating feedback for a job. */
638
+ async submitFeedback(identity, jobEventId, providerPubkey, positive, capability) {
639
+ const tags = [
640
+ ["e", jobEventId],
641
+ ["p", providerPubkey],
642
+ ["status", "success"],
643
+ ["rating", positive ? "1" : "0"],
644
+ ["t", "elisym"]
645
+ ];
646
+ if (capability) tags.push(["t", capability]);
647
+ const event = nostrTools.finalizeEvent(
648
+ {
649
+ kind: KIND_JOB_FEEDBACK,
650
+ created_at: Math.floor(Date.now() / 1e3),
651
+ tags,
652
+ content: positive ? "Good result" : "Poor result"
653
+ },
654
+ identity.secretKey
655
+ );
656
+ await this.pool.publish(event);
657
+ }
658
+ // --- Provider methods ---
659
+ /** Subscribe to incoming job requests for specific kinds. */
660
+ subscribeToJobRequests(identity, kinds, onRequest) {
661
+ return this.pool.subscribe(
662
+ {
663
+ kinds,
664
+ "#p": [identity.publicKey],
665
+ since: Math.floor(Date.now() / 1e3)
666
+ },
667
+ onRequest
668
+ );
669
+ }
670
+ /** Submit a job result with NIP-44 encrypted content. Result kind is derived from the request kind. */
671
+ async submitJobResult(identity, requestEvent, content, amount) {
672
+ const encrypted = nip44Encrypt(content, identity.secretKey, requestEvent.pubkey);
673
+ const resultKind = KIND_JOB_RESULT_BASE + (requestEvent.kind - KIND_JOB_REQUEST_BASE);
674
+ const tags = [
675
+ ["e", requestEvent.id],
676
+ ["p", requestEvent.pubkey],
677
+ ["t", "elisym"],
678
+ ["encrypted", "nip44"]
679
+ ];
680
+ if (amount != null) {
681
+ tags.push(["amount", String(amount)]);
682
+ }
683
+ const event = nostrTools.finalizeEvent(
684
+ {
685
+ kind: resultKind,
686
+ created_at: Math.floor(Date.now() / 1e3),
687
+ tags,
688
+ content: encrypted
689
+ },
690
+ identity.secretKey
691
+ );
692
+ await this.pool.publish(event);
693
+ return event.id;
694
+ }
695
+ /** Submit payment-required feedback with a payment request. */
696
+ async submitPaymentRequiredFeedback(identity, requestEvent, amount, paymentRequestJson) {
697
+ const event = nostrTools.finalizeEvent(
698
+ {
699
+ kind: KIND_JOB_FEEDBACK,
700
+ created_at: Math.floor(Date.now() / 1e3),
701
+ tags: [
702
+ ["e", requestEvent.id],
703
+ ["p", requestEvent.pubkey],
704
+ ["status", "payment-required"],
705
+ ["amount", String(amount), paymentRequestJson, "solana"],
706
+ ["t", "elisym"]
707
+ ],
708
+ content: ""
709
+ },
710
+ identity.secretKey
711
+ );
712
+ await this.pool.publish(event);
713
+ }
714
+ // --- Query methods ---
715
+ /** Fetch recent jobs from the network. */
716
+ async fetchRecentJobs(agentPubkeys, limit, since, kindOffsets) {
717
+ const offsets = kindOffsets ?? [DEFAULT_KIND_OFFSET];
718
+ const requestKinds = offsets.map(jobRequestKind);
719
+ const resultKinds = offsets.map(jobResultKind);
720
+ const reqFilter = {
721
+ kinds: requestKinds,
722
+ "#t": ["elisym"],
723
+ ...limit != null && { limit },
724
+ ...since != null && { since }
725
+ };
726
+ const requests = await this.pool.querySync(reqFilter);
727
+ const requestIds = requests.map((r) => r.id);
728
+ let results = [];
729
+ let feedbacks = [];
730
+ if (requestIds.length > 0) {
731
+ const [resultArrays, feedbackArrays] = await Promise.all([
732
+ this.pool.queryBatchedByTag(
733
+ { kinds: resultKinds },
734
+ "e",
735
+ requestIds
736
+ ),
737
+ this.pool.queryBatchedByTag(
738
+ { kinds: [KIND_JOB_FEEDBACK] },
739
+ "e",
740
+ requestIds
741
+ )
742
+ ]);
743
+ results = resultArrays;
744
+ feedbacks = feedbackArrays;
745
+ }
746
+ const targetedAgentByRequest = /* @__PURE__ */ new Map();
747
+ for (const req of requests) {
748
+ const pTag = req.tags.find((t) => t[0] === "p");
749
+ if (pTag?.[1]) targetedAgentByRequest.set(req.id, pTag[1]);
750
+ }
751
+ const resultsByRequest = /* @__PURE__ */ new Map();
752
+ for (const r of results) {
753
+ const reqId = resolveRequestId(r);
754
+ if (!reqId) continue;
755
+ const targeted = targetedAgentByRequest.get(reqId);
756
+ if (targeted && r.pubkey !== targeted) continue;
757
+ const existing = resultsByRequest.get(reqId);
758
+ if (!existing || targeted && r.pubkey === targeted) {
759
+ resultsByRequest.set(reqId, r);
760
+ }
761
+ }
762
+ const feedbackByRequest = /* @__PURE__ */ new Map();
763
+ for (const f of feedbacks) {
764
+ const reqId = resolveRequestId(f);
765
+ if (!reqId) continue;
766
+ const targeted = targetedAgentByRequest.get(reqId);
767
+ if (targeted && f.pubkey !== targeted) continue;
768
+ const existing = feedbackByRequest.get(reqId);
769
+ if (!existing || targeted && f.pubkey === targeted) {
770
+ feedbackByRequest.set(reqId, f);
771
+ }
772
+ }
773
+ const jobs = [];
774
+ for (const req of requests) {
775
+ const result = resultsByRequest.get(req.id);
776
+ const feedback = feedbackByRequest.get(req.id);
777
+ const jobAgentPubkey = result?.pubkey ?? feedback?.pubkey;
778
+ if (agentPubkeys && agentPubkeys.size > 0 && jobAgentPubkey) {
779
+ if (!agentPubkeys.has(jobAgentPubkey)) continue;
780
+ }
781
+ const capability = req.tags.find((t) => t[0] === "t" && t[1] !== "elisym")?.[1];
782
+ const bid = req.tags.find((t) => t[0] === "bid")?.[1];
783
+ let status = "processing";
784
+ let amount;
785
+ let txHash;
786
+ if (result) {
787
+ status = "success";
788
+ const amtTag = result.tags.find((t) => t[0] === "amount");
789
+ if (amtTag?.[1]) amount = parseInt(amtTag[1], 10);
790
+ }
791
+ const allFeedbacksForReq = feedbacks.filter(
792
+ (f) => resolveRequestId(f) === req.id
793
+ );
794
+ for (const fb of allFeedbacksForReq) {
795
+ const txTag = fb.tags.find((t) => t[0] === "tx");
796
+ if (txTag?.[1]) {
797
+ txHash = txTag[1];
798
+ break;
799
+ }
800
+ }
801
+ if (feedback) {
802
+ if (!result) {
803
+ const statusTag = feedback.tags.find((t) => t[0] === "status");
804
+ if (statusTag?.[1]) {
805
+ const isTargeted = targetedAgentByRequest.has(req.id);
806
+ if (statusTag[1] === "payment-required" && !bid && !isTargeted) ; else {
807
+ status = statusTag[1];
808
+ }
809
+ }
810
+ }
811
+ if (!amount) {
812
+ const amtTag = feedback.tags.find((t) => t[0] === "amount");
813
+ if (amtTag?.[1]) amount = parseInt(amtTag[1], 10);
814
+ }
815
+ }
816
+ jobs.push({
817
+ eventId: req.id,
818
+ customer: req.pubkey,
819
+ agentPubkey: jobAgentPubkey,
820
+ capability,
821
+ bid: bid ? parseInt(bid, 10) : void 0,
822
+ status,
823
+ result: result?.content,
824
+ resultEventId: result?.id,
825
+ amount,
826
+ txHash,
827
+ createdAt: req.created_at
828
+ });
829
+ }
830
+ return jobs.sort((a, b) => b.createdAt - a.createdAt);
831
+ }
832
+ /** Subscribe to live elisym events (requests, results, feedback). */
833
+ subscribeToEvents(kinds, onEvent) {
834
+ return this.pool.subscribe(
835
+ {
836
+ kinds,
837
+ "#t": ["elisym"],
838
+ since: Math.floor(Date.now() / 1e3)
839
+ },
840
+ onEvent
841
+ );
842
+ }
843
+ };
844
+ var MessagingService = class _MessagingService {
845
+ // 30s
846
+ constructor(pool) {
847
+ this.pool = pool;
848
+ this.sessionIdentity = ElisymIdentity.generate();
849
+ }
850
+ sessionIdentity;
851
+ pingCache = /* @__PURE__ */ new Map();
852
+ // pubkey → timestamp of last online result
853
+ pendingPings = /* @__PURE__ */ new Map();
854
+ // dedup in-flight pings
855
+ static PING_CACHE_TTL = 3e4;
856
+ /**
857
+ * Ping an agent via ephemeral Nostr events (kind 20200/20201).
858
+ * Uses a persistent session identity to avoid relay rate-limiting.
859
+ * Publishes to ALL relays for maximum delivery reliability.
860
+ * Caches results for 30s to prevent redundant publishes.
861
+ */
862
+ async pingAgent(agentPubkey, timeoutMs = 15e3, signal) {
863
+ const cachedAt = this.pingCache.get(agentPubkey);
864
+ if (cachedAt && Date.now() - cachedAt < _MessagingService.PING_CACHE_TTL) {
865
+ console.log(`[ping] cache hit for ${agentPubkey.slice(0, 8)}: online`);
866
+ return { online: true, identity: this.sessionIdentity };
867
+ }
868
+ const pending = this.pendingPings.get(agentPubkey);
869
+ if (pending) {
870
+ console.log(`[ping] dedup: reusing in-flight ping for ${agentPubkey.slice(0, 8)}`);
871
+ return pending;
872
+ }
873
+ const promise = this._doPing(agentPubkey, timeoutMs, signal);
874
+ this.pendingPings.set(agentPubkey, promise);
875
+ promise.finally(() => this.pendingPings.delete(agentPubkey));
876
+ return promise;
877
+ }
878
+ async _doPing(agentPubkey, timeoutMs, signal) {
879
+ const sk = this.sessionIdentity.secretKey;
880
+ const pk = this.sessionIdentity.publicKey;
881
+ const nonce = crypto.getRandomValues(new Uint8Array(16)).reduce((s, b) => s + b.toString(16).padStart(2, "0"), "");
882
+ const shortNonce = nonce.slice(0, 8);
883
+ const shortAgent = agentPubkey.slice(0, 8);
884
+ console.log(`[ping] \u2192 ping ${shortAgent} nonce=${shortNonce}`);
885
+ if (signal?.aborted) {
886
+ return { online: false, identity: null };
887
+ }
888
+ return new Promise(async (resolve) => {
889
+ let resolved = false;
890
+ const done = (online, reason) => {
891
+ if (resolved) return;
892
+ resolved = true;
893
+ clearTimeout(timer);
894
+ sub.close();
895
+ signal?.removeEventListener("abort", onAbort);
896
+ console.log(`[ping] ${online ? "\u2713 ONLINE" : "\u2717 OFFLINE"} agent=${shortAgent} nonce=${shortNonce}${reason ? ` (${reason})` : ""}`);
897
+ if (online) this.pingCache.set(agentPubkey, Date.now());
898
+ resolve({ online, identity: online ? this.sessionIdentity : null });
899
+ };
900
+ const onAbort = () => done(false, "aborted");
901
+ signal?.addEventListener("abort", onAbort);
902
+ const sub = this.pool.subscribe(
903
+ { kinds: [KIND_PONG], "#p": [pk] },
904
+ (ev) => {
905
+ try {
906
+ const msg = JSON.parse(ev.content);
907
+ if (msg.type === "elisym_pong" && msg.nonce === nonce) {
908
+ console.log(`[ping] \u2190 pong from ${ev.pubkey.slice(0, 8)} nonce=${shortNonce}`);
909
+ done(true, "pong matched");
910
+ }
911
+ } catch {
912
+ }
913
+ }
914
+ );
915
+ const pingEvent = nostrTools.finalizeEvent(
916
+ {
917
+ kind: KIND_PING,
918
+ created_at: Math.floor(Date.now() / 1e3),
919
+ tags: [["p", agentPubkey]],
920
+ content: JSON.stringify({ type: "elisym_ping", nonce })
921
+ },
922
+ sk
923
+ );
924
+ this.pool.publishAll(pingEvent).then(() => console.log(`[ping] \u2713 published nonce=${shortNonce}`)).catch((err) => {
925
+ console.error(`[ping] \u2717 publish failed nonce=${shortNonce}`, err);
926
+ done(false, "publish failed");
927
+ });
928
+ const timer = setTimeout(() => done(false, "timeout"), timeoutMs);
929
+ });
930
+ }
931
+ /**
932
+ * Subscribe to incoming ephemeral ping events (kind 20200).
933
+ * No `since` filter needed - ephemeral events are never stored.
934
+ */
935
+ subscribeToPings(identity, onPing) {
936
+ return this.pool.subscribe(
937
+ { kinds: [KIND_PING], "#p": [identity.publicKey] },
938
+ (ev) => {
939
+ try {
940
+ const msg = JSON.parse(ev.content);
941
+ if (msg.type === "elisym_ping" && msg.nonce) {
942
+ onPing(ev.pubkey, msg.nonce);
943
+ }
944
+ } catch {
945
+ }
946
+ }
947
+ );
948
+ }
949
+ /** Send an ephemeral pong response to ALL relays. */
950
+ async sendPong(identity, recipientPubkey, nonce) {
951
+ const pongEvent = nostrTools.finalizeEvent(
952
+ {
953
+ kind: KIND_PONG,
954
+ created_at: Math.floor(Date.now() / 1e3),
955
+ tags: [["p", recipientPubkey]],
956
+ content: JSON.stringify({ type: "elisym_pong", nonce })
957
+ },
958
+ identity.secretKey
959
+ );
960
+ await this.pool.publishAll(pongEvent);
961
+ }
962
+ /** Send a NIP-17 DM. */
963
+ async sendMessage(identity, recipientPubkey, content) {
964
+ const wrap = nip17__namespace.wrapEvent(
965
+ identity.secretKey,
966
+ { publicKey: recipientPubkey },
967
+ content
968
+ );
969
+ await this.pool.publish(wrap);
970
+ }
971
+ /** Fetch historical NIP-17 DMs from relays. Returns decrypted messages sorted by time. */
972
+ async fetchMessageHistory(identity, since) {
973
+ const events = await this.pool.querySync({
974
+ kinds: [KIND_GIFT_WRAP],
975
+ "#p": [identity.publicKey],
976
+ since
977
+ });
978
+ const seen = /* @__PURE__ */ new Set();
979
+ const messages = [];
980
+ for (const ev of events) {
981
+ try {
982
+ const rumor = nip59__namespace.unwrapEvent(ev, identity.secretKey);
983
+ if (seen.has(rumor.id)) continue;
984
+ seen.add(rumor.id);
985
+ messages.push({
986
+ senderPubkey: rumor.pubkey,
987
+ content: rumor.content,
988
+ createdAt: rumor.created_at,
989
+ rumorId: rumor.id
990
+ });
991
+ } catch {
992
+ }
993
+ }
994
+ return messages.sort((a, b) => a.createdAt - b.createdAt);
995
+ }
996
+ /** Subscribe to incoming NIP-17 DMs. */
997
+ subscribeToMessages(identity, onMessage, since) {
998
+ const seen = /* @__PURE__ */ new Set();
999
+ const filter = {
1000
+ kinds: [KIND_GIFT_WRAP],
1001
+ "#p": [identity.publicKey]
1002
+ };
1003
+ if (since !== void 0) filter.since = since;
1004
+ return this.pool.subscribe(
1005
+ filter,
1006
+ (ev) => {
1007
+ try {
1008
+ const rumor = nip59__namespace.unwrapEvent(ev, identity.secretKey);
1009
+ if (seen.has(rumor.id)) return;
1010
+ seen.add(rumor.id);
1011
+ onMessage(rumor.pubkey, rumor.content, rumor.created_at, rumor.id);
1012
+ } catch {
1013
+ }
1014
+ }
1015
+ );
1016
+ }
1017
+ };
1018
+ var PaymentService = class _PaymentService {
1019
+ /**
1020
+ * Calculate protocol fee using Decimal basis-point math (no floats).
1021
+ * Returns ceil(amount * PROTOCOL_FEE_BPS / 10000).
1022
+ */
1023
+ static calculateProtocolFee(amount) {
1024
+ if (amount === 0) return 0;
1025
+ return new Decimal__default.default(amount).mul(PROTOCOL_FEE_BPS).div(1e4).toDecimalPlaces(0, Decimal__default.default.ROUND_CEIL).toNumber();
1026
+ }
1027
+ /**
1028
+ * Validate that a payment request has the correct recipient and protocol fee.
1029
+ * Returns an error message if invalid, null if OK.
1030
+ */
1031
+ static validatePaymentFee(requestJson, expectedRecipient) {
1032
+ let data;
1033
+ try {
1034
+ data = JSON.parse(requestJson);
1035
+ } catch (e) {
1036
+ return `Invalid payment request JSON: ${e}`;
1037
+ }
1038
+ if (expectedRecipient && data.recipient !== expectedRecipient) {
1039
+ return `Recipient mismatch: expected ${expectedRecipient}, got ${data.recipient}. Provider may be attempting to redirect payment.`;
1040
+ }
1041
+ if (data.created_at > 0 && data.expiry_secs > 0) {
1042
+ const elapsed = Math.floor(Date.now() / 1e3) - data.created_at;
1043
+ if (elapsed > data.expiry_secs) {
1044
+ return `Payment request expired (created ${data.created_at}, expiry ${data.expiry_secs}s).`;
1045
+ }
1046
+ }
1047
+ const expectedFee = _PaymentService.calculateProtocolFee(data.amount);
1048
+ const { fee_address, fee_amount } = data;
1049
+ if (fee_address && fee_amount && fee_amount > 0) {
1050
+ if (fee_address !== PROTOCOL_TREASURY) {
1051
+ return `Fee address mismatch: expected ${PROTOCOL_TREASURY}, got ${fee_address}. Provider may be attempting to redirect fees.`;
1052
+ }
1053
+ if (fee_amount !== expectedFee) {
1054
+ return `Fee amount mismatch: expected ${expectedFee} lamports (${PROTOCOL_FEE_BPS}bps of ${data.amount}), got ${fee_amount}. Provider may be tampering with fee.`;
1055
+ }
1056
+ return null;
1057
+ }
1058
+ if (!fee_address && !fee_amount) {
1059
+ return `Payment request missing protocol fee (${PROTOCOL_FEE_BPS}bps). Expected fee: ${expectedFee} lamports to ${PROTOCOL_TREASURY}.`;
1060
+ }
1061
+ return `Invalid fee params in payment request. Expected fee: ${expectedFee} lamports to ${PROTOCOL_TREASURY}.`;
1062
+ }
1063
+ /**
1064
+ * Build a Solana transaction from a payment request.
1065
+ * The caller must sign and send via wallet adapter.
1066
+ */
1067
+ static buildPaymentTransaction(payerPubkey, paymentRequest) {
1068
+ const recipient = new web3_js.PublicKey(paymentRequest.recipient);
1069
+ const reference = new web3_js.PublicKey(paymentRequest.reference);
1070
+ const feeAddress = paymentRequest.fee_address ? new web3_js.PublicKey(paymentRequest.fee_address) : null;
1071
+ const feeAmount = paymentRequest.fee_amount ?? 0;
1072
+ const providerAmount = feeAddress && feeAmount > 0 ? new Decimal__default.default(paymentRequest.amount).minus(feeAmount).toNumber() : paymentRequest.amount;
1073
+ const transferIx = web3_js.SystemProgram.transfer({
1074
+ fromPubkey: payerPubkey,
1075
+ toPubkey: recipient,
1076
+ lamports: providerAmount
1077
+ });
1078
+ transferIx.keys.push({
1079
+ pubkey: reference,
1080
+ isSigner: false,
1081
+ isWritable: false
1082
+ });
1083
+ const tx = new web3_js.Transaction().add(transferIx);
1084
+ if (feeAddress && feeAmount > 0) {
1085
+ tx.add(
1086
+ web3_js.SystemProgram.transfer({
1087
+ fromPubkey: payerPubkey,
1088
+ toPubkey: feeAddress,
1089
+ lamports: feeAmount
1090
+ })
1091
+ );
1092
+ }
1093
+ return tx;
1094
+ }
1095
+ /**
1096
+ * Create a payment request with auto-calculated protocol fee.
1097
+ * Used by providers to generate payment requests for customers.
1098
+ */
1099
+ static createPaymentRequest(recipientAddress, amount, expirySecs = 600) {
1100
+ const feeAmount = _PaymentService.calculateProtocolFee(amount);
1101
+ const reference = web3_js.PublicKey.unique().toBase58();
1102
+ return {
1103
+ recipient: recipientAddress,
1104
+ amount,
1105
+ reference,
1106
+ fee_address: PROTOCOL_TREASURY,
1107
+ fee_amount: feeAmount,
1108
+ created_at: Math.floor(Date.now() / 1e3),
1109
+ expiry_secs: expirySecs
1110
+ };
1111
+ }
1112
+ };
1113
+ function formatSol(lamports) {
1114
+ const sol = new Decimal__default.default(lamports).div(LAMPORTS_PER_SOL);
1115
+ if (sol.gte(1e6)) return `${sol.idiv(1e6)}m SOL`;
1116
+ if (sol.gte(1e4)) return `${sol.idiv(1e3)}k SOL`;
1117
+ return `${compactSol(sol)} SOL`;
1118
+ }
1119
+ function compactSol(sol) {
1120
+ if (sol.isZero()) return "0";
1121
+ if (sol.gte(1e3)) return sol.toDecimalPlaces(0, Decimal__default.default.ROUND_FLOOR).toString();
1122
+ const maxFrac = 9;
1123
+ for (let d = 1; d <= maxFrac; d++) {
1124
+ const s = sol.toFixed(d);
1125
+ if (new Decimal__default.default(s).eq(sol)) {
1126
+ return s.replace(/0+$/, "").replace(/\.$/, "");
1127
+ }
1128
+ }
1129
+ return sol.toFixed(maxFrac).replace(/0+$/, "").replace(/\.$/, "");
1130
+ }
1131
+ function timeAgo(unix) {
1132
+ const seconds = Math.max(0, Math.floor(Date.now() / 1e3 - unix));
1133
+ if (seconds < 60) return `${seconds}s ago`;
1134
+ const minutes = Math.floor(seconds / 60);
1135
+ if (minutes < 60) return `${minutes}m ago`;
1136
+ const hours = Math.floor(minutes / 60);
1137
+ if (hours < 24) return `${hours}h ago`;
1138
+ const days = Math.floor(hours / 24);
1139
+ return `${days}d ago`;
1140
+ }
1141
+ function truncateKey(hex, chars = 6) {
1142
+ if (hex.length <= chars * 2) return hex;
1143
+ return `${hex.slice(0, chars)}...${hex.slice(-chars)}`;
1144
+ }
1145
+ function makeNjumpUrl(eventId, relays = RELAYS) {
1146
+ const nevent = nostrTools.nip19.neventEncode({
1147
+ id: eventId,
1148
+ relays: relays.slice(0, 2)
1149
+ });
1150
+ return `https://njump.me/${nevent}`;
1151
+ }
1152
+
1153
+ // src/index.ts
1154
+ var ElisymClient = class {
1155
+ pool;
1156
+ discovery;
1157
+ marketplace;
1158
+ messaging;
1159
+ constructor(config = {}) {
1160
+ this.pool = new NostrPool(config.relays ?? RELAYS);
1161
+ this.discovery = new DiscoveryService(this.pool);
1162
+ this.marketplace = new MarketplaceService(this.pool);
1163
+ this.messaging = new MessagingService(this.pool);
1164
+ }
1165
+ close() {
1166
+ this.pool.close();
1167
+ }
1168
+ };
1169
+
1170
+ exports.DEFAULT_KIND_OFFSET = DEFAULT_KIND_OFFSET;
1171
+ exports.DiscoveryService = DiscoveryService;
1172
+ exports.ElisymClient = ElisymClient;
1173
+ exports.ElisymIdentity = ElisymIdentity;
1174
+ exports.KIND_APP_HANDLER = KIND_APP_HANDLER;
1175
+ exports.KIND_GIFT_WRAP = KIND_GIFT_WRAP;
1176
+ exports.KIND_JOB_FEEDBACK = KIND_JOB_FEEDBACK;
1177
+ exports.KIND_JOB_REQUEST = KIND_JOB_REQUEST;
1178
+ exports.KIND_JOB_REQUEST_BASE = KIND_JOB_REQUEST_BASE;
1179
+ exports.KIND_JOB_RESULT = KIND_JOB_RESULT;
1180
+ exports.KIND_JOB_RESULT_BASE = KIND_JOB_RESULT_BASE;
1181
+ exports.KIND_PING = KIND_PING;
1182
+ exports.KIND_PONG = KIND_PONG;
1183
+ exports.LAMPORTS_PER_SOL = LAMPORTS_PER_SOL;
1184
+ exports.MarketplaceService = MarketplaceService;
1185
+ exports.MessagingService = MessagingService;
1186
+ exports.NostrPool = NostrPool;
1187
+ exports.PROTOCOL_FEE_BPS = PROTOCOL_FEE_BPS;
1188
+ exports.PROTOCOL_TREASURY = PROTOCOL_TREASURY;
1189
+ exports.PaymentService = PaymentService;
1190
+ exports.RELAYS = RELAYS;
1191
+ exports.formatSol = formatSol;
1192
+ exports.jobRequestKind = jobRequestKind;
1193
+ exports.jobResultKind = jobResultKind;
1194
+ exports.makeNjumpUrl = makeNjumpUrl;
1195
+ exports.timeAgo = timeAgo;
1196
+ exports.toDTag = toDTag;
1197
+ exports.truncateKey = truncateKey;
1198
+ //# sourceMappingURL=index.cjs.map
1199
+ //# sourceMappingURL=index.cjs.map