@toon-protocol/client-mcp 0.30.0 → 0.31.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 (30) hide show
  1. package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js → anon-proxy-W3KMM7GU-FN7ZJY7P.js} +2 -1
  2. package/dist/chunk-4K6S3VPM.js +1567 -0
  3. package/dist/chunk-4K6S3VPM.js.map +1 -0
  4. package/dist/chunk-F22GNSF6.js +12 -0
  5. package/dist/{chunk-TEMKXTNH.js → chunk-QHNSETFB.js} +18 -8
  6. package/dist/{chunk-TEMKXTNH.js.map → chunk-QHNSETFB.js.map} +1 -1
  7. package/dist/{chunk-CH37M2I2.js → chunk-U227QSQG.js} +33 -9
  8. package/dist/chunk-U227QSQG.js.map +1 -0
  9. package/dist/daemon.js +3 -2
  10. package/dist/daemon.js.map +1 -1
  11. package/dist/{ed25519-OFFWPWRE.js → ed25519-UB2JDJJK.js} +2 -1
  12. package/dist/{gateway-QOK47RKS-HB65KIKC.js → gateway-QOK47RKS-SEGTXBR3.js} +2 -1
  13. package/dist/{gateway-QOK47RKS-HB65KIKC.js.map → gateway-QOK47RKS-SEGTXBR3.js.map} +1 -1
  14. package/dist/{hmac-7WSXTWW4.js → hmac-26UC6YKX.js} +2 -1
  15. package/dist/hmac-26UC6YKX.js.map +1 -0
  16. package/dist/index.d.ts +72 -9
  17. package/dist/index.js +4 -3
  18. package/dist/mcp.js +3 -2
  19. package/dist/mcp.js.map +1 -1
  20. package/dist/{sha512-LMOIUNFJ.js → sha512-WYC446ZM.js} +2 -1
  21. package/dist/{sha512-LMOIUNFJ.js.map → sha512-WYC446ZM.js.map} +1 -1
  22. package/dist/{socks5-WTJBYGME-IXWLQDE7.js → socks5-WTJBYGME-6COK4LXW.js} +2 -1
  23. package/dist/{socks5-WTJBYGME-IXWLQDE7.js.map → socks5-WTJBYGME-6COK4LXW.js.map} +1 -1
  24. package/package.json +3 -2
  25. package/dist/chunk-2MY6AQK6.js +0 -667
  26. package/dist/chunk-2MY6AQK6.js.map +0 -1
  27. package/dist/chunk-CH37M2I2.js.map +0 -1
  28. /package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js.map → anon-proxy-W3KMM7GU-FN7ZJY7P.js.map} +0 -0
  29. /package/dist/{ed25519-OFFWPWRE.js.map → chunk-F22GNSF6.js.map} +0 -0
  30. /package/dist/{hmac-7WSXTWW4.js.map → ed25519-UB2JDJJK.js.map} +0 -0
@@ -1,667 +0,0 @@
1
- import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
- import {
3
- decodeEventFromToon
4
- } from "./chunk-TEMKXTNH.js";
5
-
6
- // src/relay-subscription.ts
7
- import { createRequire } from "module";
8
- var nodeRequire = createRequire(import.meta.url);
9
- var DEFAULT_BUFFER = 5e3;
10
- var DEFAULT_BASE_MS = 1e3;
11
- var DEFAULT_MAX_MS = 3e4;
12
- var noop = () => void 0;
13
- var RelaySubscription = class {
14
- relayUrl;
15
- socksProxy;
16
- bufferSize;
17
- reconnectBaseMs;
18
- reconnectMaxMs;
19
- log;
20
- wsFactory;
21
- decodeEvent;
22
- /** Active subscriptions: subId -> filters (re-sent on every (re)connect). */
23
- subscriptions = /* @__PURE__ */ new Map();
24
- /** Ring buffer of received events, ordered by ascending `seq`. */
25
- buffer = [];
26
- /** De-dup index: event.id -> seq (kept in lockstep with the buffer). */
27
- seen = /* @__PURE__ */ new Set();
28
- seqCounter = 0;
29
- subIdCounter = 0;
30
- ws = null;
31
- connected = false;
32
- closing = false;
33
- reconnectAttempts = 0;
34
- reconnectTimer = null;
35
- constructor(opts) {
36
- this.relayUrl = opts.relayUrl;
37
- this.socksProxy = opts.socksProxy;
38
- this.bufferSize = opts.bufferSize ?? DEFAULT_BUFFER;
39
- this.reconnectBaseMs = opts.reconnectBaseMs ?? DEFAULT_BASE_MS;
40
- this.reconnectMaxMs = opts.reconnectMaxMs ?? DEFAULT_MAX_MS;
41
- this.log = opts.logger ?? noop;
42
- this.wsFactory = opts.wsFactory ?? defaultWebSocketFactory(this.socksProxy);
43
- this.decodeEvent = opts.decodeEvent;
44
- }
45
- /** Whether the underlying socket is currently open. */
46
- isConnected() {
47
- return this.connected;
48
- }
49
- /** Number of events currently held in the buffer. */
50
- bufferedCount() {
51
- return this.buffer.length;
52
- }
53
- /** Active subscription ids. */
54
- activeSubscriptions() {
55
- return [...this.subscriptions.keys()];
56
- }
57
- /** Open the connection (idempotent). */
58
- start() {
59
- this.closing = false;
60
- if (this.ws) return;
61
- this.open();
62
- }
63
- /**
64
- * Register a persistent subscription and (if connected) send the REQ.
65
- * Returns the subscription id (caller-supplied or generated).
66
- */
67
- subscribe(filters, subId) {
68
- const id = subId ?? `sub-${++this.subIdCounter}`;
69
- const list = Array.isArray(filters) ? filters : [filters];
70
- this.subscriptions.set(id, list);
71
- if (this.connected) this.sendReq(id, list);
72
- return id;
73
- }
74
- /** Cancel a subscription and send CLOSE if connected. */
75
- unsubscribe(subId) {
76
- if (!this.subscriptions.delete(subId)) return;
77
- if (this.connected) this.sendRaw(["CLOSE", subId]);
78
- }
79
- /**
80
- * Drain events newer than `cursor`. The cursor is the highest `seq` returned;
81
- * pass it back to fetch only events received since. Filtering by `subId`
82
- * restricts to one subscription.
83
- */
84
- getEvents(opts = {}) {
85
- const after = opts.cursor ?? 0;
86
- const limit = opts.limit ?? 200;
87
- const matches = this.buffer.filter(
88
- (b) => b.seq > after && (opts.subId === void 0 || b.subId === opts.subId)
89
- );
90
- const page = matches.slice(0, limit);
91
- const hasMore = matches.length > page.length;
92
- const last = page.at(-1);
93
- const cursor = last ? last.seq : after;
94
- return { events: page.map((b) => b.event), cursor, hasMore };
95
- }
96
- /** Close the connection permanently and stop reconnecting. */
97
- close() {
98
- this.closing = true;
99
- if (this.reconnectTimer) {
100
- clearTimeout(this.reconnectTimer);
101
- this.reconnectTimer = null;
102
- }
103
- if (this.ws) {
104
- try {
105
- this.ws.close();
106
- } catch {
107
- }
108
- this.ws = null;
109
- }
110
- this.connected = false;
111
- }
112
- // ── internals ────────────────────────────────────────────────────────────
113
- open() {
114
- let ws;
115
- try {
116
- ws = this.wsFactory(this.relayUrl);
117
- } catch (err) {
118
- this.log(`[relay] connect failed: ${errMsg(err)}`);
119
- this.scheduleReconnect();
120
- return;
121
- }
122
- this.ws = ws;
123
- ws.on("open", () => {
124
- this.connected = true;
125
- this.reconnectAttempts = 0;
126
- this.log(`[relay] connected to ${this.relayUrl}`);
127
- for (const [id, filters] of this.subscriptions) this.sendReq(id, filters);
128
- });
129
- ws.on("message", (data) => this.handleMessage(data));
130
- ws.on("error", (err) => {
131
- this.log(`[relay] socket error: ${errMsg(err)}`);
132
- });
133
- ws.on("close", () => {
134
- this.connected = false;
135
- this.ws = null;
136
- if (!this.closing) {
137
- this.log("[relay] disconnected; scheduling reconnect");
138
- this.scheduleReconnect();
139
- }
140
- });
141
- }
142
- scheduleReconnect() {
143
- if (this.closing || this.reconnectTimer) return;
144
- const delay = Math.min(
145
- this.reconnectMaxMs,
146
- this.reconnectBaseMs * 2 ** this.reconnectAttempts
147
- );
148
- this.reconnectAttempts += 1;
149
- this.reconnectTimer = setTimeout(() => {
150
- this.reconnectTimer = null;
151
- if (!this.closing) this.open();
152
- }, delay);
153
- this.reconnectTimer.unref?.();
154
- }
155
- sendReq(subId, filters) {
156
- this.sendRaw(["REQ", subId, ...filters]);
157
- }
158
- sendRaw(message) {
159
- if (!this.ws || !this.connected) return;
160
- try {
161
- this.ws.send(JSON.stringify(message));
162
- } catch (err) {
163
- this.log(`[relay] send failed: ${errMsg(err)}`);
164
- }
165
- }
166
- handleMessage(data) {
167
- let parsed;
168
- try {
169
- parsed = JSON.parse(toText(data));
170
- } catch {
171
- return;
172
- }
173
- if (!Array.isArray(parsed) || parsed.length === 0) return;
174
- const type = parsed[0];
175
- switch (type) {
176
- case "EVENT": {
177
- const subId = typeof parsed[1] === "string" ? parsed[1] : "";
178
- const event = this.parseEventPayload(parsed[2]);
179
- if (event && typeof event.id === "string")
180
- this.bufferEvent(subId, event);
181
- break;
182
- }
183
- case "EOSE":
184
- break;
185
- case "CLOSED":
186
- this.log(
187
- `[relay] subscription closed by relay: ${String(parsed[2] ?? "")}`
188
- );
189
- break;
190
- case "NOTICE":
191
- this.log(`[relay] NOTICE: ${String(parsed[1] ?? "")}`);
192
- break;
193
- default:
194
- break;
195
- }
196
- }
197
- /**
198
- * Normalise an `EVENT` payload to a NostrEvent. Standard relays send a JSON
199
- * object; the TOON relay sends a TOON-encoded string, decoded via the injected
200
- * `decodeEvent`. Returns undefined when it can't be parsed.
201
- */
202
- parseEventPayload(raw) {
203
- if (raw && typeof raw === "object" && typeof raw.id === "string") {
204
- return raw;
205
- }
206
- if (typeof raw === "string" && this.decodeEvent) {
207
- try {
208
- return this.decodeEvent(raw);
209
- } catch (err) {
210
- this.log(`[relay] event decode failed: ${errMsg(err)}`);
211
- return void 0;
212
- }
213
- }
214
- return void 0;
215
- }
216
- bufferEvent(subId, event) {
217
- if (this.seen.has(event.id)) return;
218
- this.seen.add(event.id);
219
- this.buffer.push({ seq: ++this.seqCounter, subId, event });
220
- if (this.buffer.length > this.bufferSize) {
221
- const evicted = this.buffer.shift();
222
- if (evicted) this.seen.delete(evicted.event.id);
223
- }
224
- }
225
- };
226
- function toText(data) {
227
- if (typeof data === "string") return data;
228
- if (data instanceof Uint8Array) return Buffer.from(data).toString("utf8");
229
- if (Buffer.isBuffer(data)) return data.toString("utf8");
230
- return String(data);
231
- }
232
- function errMsg(err) {
233
- return err instanceof Error ? err.message : String(err);
234
- }
235
- function defaultWebSocketFactory(socksProxy) {
236
- return (url) => {
237
- const WebSocketImpl = nodeRequire("ws");
238
- let agent;
239
- if (socksProxy) {
240
- const { SocksProxyAgent } = nodeRequire("socks-proxy-agent");
241
- agent = new SocksProxyAgent(socksProxy);
242
- }
243
- return new WebSocketImpl(url, agent ? { agent } : void 0);
244
- };
245
- }
246
-
247
- // src/daemon/apex-channel-store.ts
248
- import { mkdirSync, readFileSync, writeFileSync } from "fs";
249
- import { dirname } from "path";
250
- function key(destination, chain) {
251
- return `${destination}|${chain}`;
252
- }
253
- function readStore(path) {
254
- try {
255
- return JSON.parse(readFileSync(path, "utf8"));
256
- } catch {
257
- return {};
258
- }
259
- }
260
- function loadApexChannel(path, destination, chain) {
261
- return readStore(path)[key(destination, chain)] ?? null;
262
- }
263
- function saveApexChannel(path, destination, chain, record) {
264
- const store = readStore(path);
265
- store[key(destination, chain)] = record;
266
- mkdirSync(dirname(path), { recursive: true });
267
- writeFileSync(path, JSON.stringify(store, null, 2), { mode: 384 });
268
- }
269
-
270
- // src/daemon/client-runner.ts
271
- var ClientRunner = class {
272
- config;
273
- client;
274
- relay;
275
- log;
276
- startedAt = Date.now();
277
- bootstrapping = false;
278
- ready = false;
279
- lastError;
280
- /** Channel opened against the default apex destination. */
281
- apexChannelId;
282
- stopped = false;
283
- constructor(deps) {
284
- this.config = deps.config;
285
- this.client = deps.createClient();
286
- this.relay = deps.createRelay?.() ?? new RelaySubscription({
287
- relayUrl: deps.config.relayUrl,
288
- socksProxy: deps.config.socksProxy,
289
- logger: deps.logger,
290
- // The TOON relay sends events TOON-encoded (text) on reads, not as JSON.
291
- decodeEvent: (raw) => decodeEventFromToon(new TextEncoder().encode(raw))
292
- });
293
- this.log = deps.logger ?? (() => void 0);
294
- }
295
- /**
296
- * Begin bootstrapping in the background. Resolves once kicked off; the
297
- * connection becomes ready asynchronously. Awaitable for tests via the
298
- * returned promise of the underlying work.
299
- */
300
- start() {
301
- if (this.bootstrapping || this.ready) return;
302
- this.bootstrapping = true;
303
- this.relay.start();
304
- void this.bootstrap();
305
- }
306
- /** The background bootstrap routine (exposed for awaiting in tests). */
307
- async bootstrap() {
308
- try {
309
- await this.client.start();
310
- this.injectApexNegotiation(this.config.apex);
311
- this.apexChannelId = await this.openOrResumeApexChannel();
312
- this.routeChildPeersThroughApexChannel();
313
- this.ready = true;
314
- this.lastError = void 0;
315
- this.log(`[runner] ready; apex channel ${this.apexChannelId}`);
316
- } catch (err) {
317
- this.lastError = err instanceof Error ? err.message : String(err);
318
- this.log(`[runner] bootstrap failed: ${this.lastError}`);
319
- } finally {
320
- this.bootstrapping = false;
321
- }
322
- }
323
- /**
324
- * Open the apex channel — or, on a restart, RESUME the existing one.
325
- *
326
- * `ChannelManager` persists the nonce watermark (by channelId) but not the
327
- * peer→channelId mapping, so a naive `openChannel()` after restart re-deposits
328
- * into a fresh channel and reverts on-chain. We persist the channelId here and,
329
- * when present, `trackChannel()` the live channel (which rehydrates the nonce
330
- * from the channel store) — no on-chain write, watermark continues.
331
- */
332
- async openOrResumeApexChannel() {
333
- const { destination, chain, apexChannelStorePath } = this.config;
334
- const saved = loadApexChannel(apexChannelStorePath, destination, chain);
335
- const cm = this.client.channelManager;
336
- if (saved && cm && typeof cm.trackChannel === "function") {
337
- cm.trackChannel(saved.channelId, saved.context);
338
- this.log(
339
- `[runner] resumed apex channel ${saved.channelId} (tracked, no re-deposit)`
340
- );
341
- return saved.channelId;
342
- }
343
- const channelId = await this.client.openChannel(destination);
344
- if (this.config.apex) {
345
- const a = this.config.apex;
346
- saveApexChannel(apexChannelStorePath, destination, chain, {
347
- channelId,
348
- context: {
349
- chainType: a.chain,
350
- chainId: a.chainId,
351
- tokenNetworkAddress: a.tokenNetwork ?? "",
352
- tokenAddress: a.tokenAddress,
353
- recipient: a.settlementAddress
354
- }
355
- });
356
- }
357
- return channelId;
358
- }
359
- /**
360
- * Inject the apex settlement negotiation directly into the ToonClient when
361
- * configured. Required for HS / direct-apex mode where bootstrap discovers 0
362
- * peers (no relay-based discovery). Mirrors the docker entrypoint's approach.
363
- */
364
- injectApexNegotiation(apex) {
365
- if (!apex) return;
366
- const negotiations = this.client.peerNegotiations;
367
- if (!(negotiations instanceof Map)) {
368
- throw new Error(
369
- "ToonClient.peerNegotiations layout changed \u2014 cannot inject apex negotiation"
370
- );
371
- }
372
- negotiations.set(apex.peerId, {
373
- chain: apex.chainKey,
374
- chainType: apex.chain,
375
- chainId: apex.chainId,
376
- settlementAddress: apex.settlementAddress,
377
- tokenAddress: apex.tokenAddress,
378
- tokenNetwork: apex.tokenNetwork
379
- });
380
- this.log(`[runner] injected apex negotiation for peer "${apex.peerId}"`);
381
- }
382
- /**
383
- * Route additional apex CHILD peers (e.g. `dvm`, `mill`) through the SAME
384
- * apex payment channel. In the parent→child apex model the client holds ONE
385
- * channel with the apex (g.townhouse) and pays via it regardless of which
386
- * child the ILP destination addresses; but `ToonClient.resolvePeerId` keys off
387
- * the destination's last segment (`town`/`dvm`/`mill`), so without this each
388
- * child would (a) fail the "no negotiation for peer" guard and (b) try to open
389
- * a SECOND on-chain channel to the same apex receive (which reverts —
390
- * channel-exists). So: inject the same apex negotiation under each child peer
391
- * AND pre-map its peer→channel to the already-open apex channel so
392
- * `ensureChannel` reuses it (no second open; one shared nonce sequence).
393
- */
394
- routeChildPeersThroughApexChannel() {
395
- const apex = this.config.apex;
396
- const children = this.config.apexChildPeers ?? [];
397
- if (!apex || !this.apexChannelId || children.length === 0) return;
398
- const client = this.client;
399
- const negotiations = client.peerNegotiations;
400
- const peerChannels = client.channelManager?.peerChannels;
401
- if (!(negotiations instanceof Map) || !(peerChannels instanceof Map)) {
402
- this.log(
403
- "[runner] cannot route child peers \u2014 ToonClient internals layout changed"
404
- );
405
- return;
406
- }
407
- for (const peer of children) {
408
- if (peer === apex.peerId) continue;
409
- negotiations.set(peer, {
410
- chain: apex.chainKey,
411
- chainType: apex.chain,
412
- chainId: apex.chainId,
413
- settlementAddress: apex.settlementAddress,
414
- tokenAddress: apex.tokenAddress,
415
- tokenNetwork: apex.tokenNetwork
416
- });
417
- peerChannels.set(peer, this.apexChannelId);
418
- this.log(
419
- `[runner] routed child peer "${peer}" through apex channel ${this.apexChannelId}`
420
- );
421
- }
422
- }
423
- isReady() {
424
- return this.ready;
425
- }
426
- isBootstrapping() {
427
- return this.bootstrapping;
428
- }
429
- getStatus() {
430
- const net = this.client.getNetworkStatus();
431
- const network = net ? ["evm", "solana", "mina"].map((c) => ({
432
- chain: c,
433
- ready: net[c] === "configured",
434
- detail: net[c]
435
- })) : void 0;
436
- return {
437
- uptimeMs: Date.now() - this.startedAt,
438
- bootstrapping: this.bootstrapping,
439
- ready: this.ready,
440
- settlementChain: this.config.chain,
441
- identity: {
442
- nostrPubkey: safe(() => this.client.getPublicKey()) ?? "",
443
- evmAddress: safe(() => this.client.getEvmAddress()),
444
- solanaAddress: safe(() => this.client.getSolanaAddress()),
445
- minaAddress: safe(() => this.client.getMinaAddress())
446
- },
447
- transport: {
448
- type: this.config.socksProxy ? "socks5" : "direct",
449
- socksProxy: this.config.socksProxy,
450
- btpUrl: this.config.toonClientConfig.btpUrl
451
- },
452
- relay: {
453
- url: this.config.relayUrl,
454
- connected: this.relay.isConnected(),
455
- buffered: this.relay.bufferedCount(),
456
- subscriptions: this.relay.activeSubscriptions()
457
- },
458
- ...network ? { network } : {},
459
- ...this.lastError ? { lastError: this.lastError } : {}
460
- };
461
- }
462
- /** Pay-to-write a single event. Throws NOT_READY while bootstrapping. */
463
- async publish(req) {
464
- this.assertReady();
465
- const channelId = this.apexChannelId ?? await this.client.openChannel(req.destination);
466
- const fee = req.fee !== void 0 ? BigInt(req.fee) : this.config.feePerEvent;
467
- const claim = await this.client.signBalanceProof(channelId, fee);
468
- const result = await this.client.publishEvent(req.event, {
469
- destination: req.destination,
470
- claim,
471
- ilpAmount: fee
472
- });
473
- if (!result.success) {
474
- throw new PublishRejectedError(result.error ?? "relay rejected event");
475
- }
476
- return {
477
- eventId: result.eventId ?? req.event.id,
478
- data: result.data,
479
- channelId,
480
- nonce: this.client.getChannelNonce(channelId)
481
- };
482
- }
483
- /** Register a free-read subscription (does not require the paid path). */
484
- subscribe(req) {
485
- const subId = this.relay.subscribe(req.filters, req.subId);
486
- return { subId };
487
- }
488
- /** Drain buffered events newer than the cursor (free read). */
489
- getEvents(query) {
490
- const opts = {};
491
- if (query.subId !== void 0) opts.subId = query.subId;
492
- if (query.cursor !== void 0) opts.cursor = query.cursor;
493
- if (query.limit !== void 0) opts.limit = query.limit;
494
- const { events, cursor, hasMore } = this.relay.getEvents(opts);
495
- return { events, cursor, hasMore };
496
- }
497
- /** Open (or return) a payment channel for a destination. */
498
- async openChannel(destination) {
499
- this.assertReady();
500
- const channelId = await this.client.openChannel(
501
- destination ?? this.config.destination
502
- );
503
- if (!destination || destination === this.config.destination) {
504
- this.apexChannelId = channelId;
505
- }
506
- return { channelId };
507
- }
508
- /** List tracked channels with their nonce watermark + cumulative amount. */
509
- getChannels() {
510
- const channels = this.client.getTrackedChannels().map((channelId) => ({
511
- channelId,
512
- nonce: this.client.getChannelNonce(channelId),
513
- cumulativeAmount: this.client.getChannelCumulativeAmount(channelId).toString()
514
- }));
515
- return { channels };
516
- }
517
- /** Send a swap packet to a mill peer. */
518
- async swap(req) {
519
- this.assertReady();
520
- const toonData = req.toonData ? new Uint8Array(Buffer.from(req.toonData, "base64")) : new Uint8Array(0);
521
- const result = await this.client.sendSwapPacket({
522
- destination: req.destination,
523
- amount: BigInt(req.amount),
524
- toonData
525
- });
526
- return {
527
- accepted: result.accepted,
528
- data: result.data,
529
- code: result.code,
530
- message: result.message
531
- };
532
- }
533
- /** Graceful teardown of the relay subscription + ToonClient. */
534
- async stop() {
535
- if (this.stopped) return;
536
- this.stopped = true;
537
- this.relay.close();
538
- try {
539
- await this.client.stop();
540
- } catch (err) {
541
- this.log(
542
- `[runner] client stop error: ${err instanceof Error ? err.message : String(err)}`
543
- );
544
- }
545
- }
546
- assertReady() {
547
- if (!this.ready) {
548
- throw new NotReadyError(
549
- this.bootstrapping ? "Daemon is still bootstrapping (BTP/anon coming up) \u2014 retry shortly." : this.lastError ?? "Daemon is not ready."
550
- );
551
- }
552
- }
553
- };
554
- var NotReadyError = class extends Error {
555
- retryable = true;
556
- constructor(message) {
557
- super(message);
558
- this.name = "NotReadyError";
559
- }
560
- };
561
- var PublishRejectedError = class extends Error {
562
- constructor(message) {
563
- super(message);
564
- this.name = "PublishRejectedError";
565
- }
566
- };
567
- function safe(fn) {
568
- try {
569
- return fn();
570
- } catch {
571
- return void 0;
572
- }
573
- }
574
-
575
- // src/daemon/routes.ts
576
- function registerRoutes(app, runner) {
577
- app.get("/status", async () => runner.getStatus());
578
- app.post("/publish", async (req, reply) => {
579
- const body = req.body;
580
- if (!body || !isSignedEvent(body.event)) {
581
- return sendError(reply, 400, "invalid_event", {
582
- detail: "body.event must be a fully-signed Nostr event (id + sig)."
583
- });
584
- }
585
- try {
586
- return await runner.publish(body);
587
- } catch (err) {
588
- return mapError(reply, err);
589
- }
590
- });
591
- app.post("/subscribe", async (req, reply) => {
592
- const body = req.body;
593
- if (!body || body.filters === void 0) {
594
- return sendError(reply, 400, "invalid_filters", {
595
- detail: "body.filters is required (a NIP-01 filter or array of filters)."
596
- });
597
- }
598
- return runner.subscribe(body);
599
- });
600
- app.get(
601
- "/events",
602
- async (req) => {
603
- const q = req.query;
604
- const query = {};
605
- if (q.subId) query.subId = q.subId;
606
- if (q.cursor !== void 0) query.cursor = Number(q.cursor);
607
- if (q.limit !== void 0) query.limit = Number(q.limit);
608
- return runner.getEvents(query);
609
- }
610
- );
611
- app.post("/channels", async (req, reply) => {
612
- try {
613
- return await runner.openChannel(req.body?.destination);
614
- } catch (err) {
615
- return mapError(reply, err);
616
- }
617
- });
618
- app.get("/channels", async () => runner.getChannels());
619
- app.post("/swap", async (req, reply) => {
620
- const body = req.body;
621
- if (!body || !body.destination || body.amount === void 0) {
622
- return sendError(reply, 400, "invalid_swap", {
623
- detail: "body.destination and body.amount are required."
624
- });
625
- }
626
- try {
627
- return await runner.swap(body);
628
- } catch (err) {
629
- return mapError(reply, err);
630
- }
631
- });
632
- }
633
- function isSignedEvent(event) {
634
- if (typeof event !== "object" || event === null) return false;
635
- const e = event;
636
- return typeof e["id"] === "string" && typeof e["sig"] === "string" && typeof e["pubkey"] === "string" && typeof e["kind"] === "number";
637
- }
638
- function mapError(reply, err) {
639
- if (err instanceof NotReadyError) {
640
- return sendError(reply, 503, "bootstrapping", {
641
- detail: err.message,
642
- retryable: true
643
- });
644
- }
645
- if (err instanceof PublishRejectedError) {
646
- return sendError(reply, 502, "rejected", { detail: err.message });
647
- }
648
- return sendError(reply, 500, "internal_error", {
649
- detail: err instanceof Error ? err.message : String(err)
650
- });
651
- }
652
- function sendError(reply, status, error, extra = {}) {
653
- return reply.status(status).send({
654
- error,
655
- ...extra.detail ? { detail: extra.detail } : {},
656
- ...extra.retryable ? { retryable: true } : {}
657
- });
658
- }
659
-
660
- export {
661
- RelaySubscription,
662
- ClientRunner,
663
- NotReadyError,
664
- PublishRejectedError,
665
- registerRoutes
666
- };
667
- //# sourceMappingURL=chunk-2MY6AQK6.js.map