@toon-protocol/client-mcp 0.1.0 → 0.3.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 (36) hide show
  1. package/README.md +19 -38
  2. package/dist/app/index.html +166 -0
  3. package/dist/{chunk-5KFXUT5Q.js → chunk-CQ2QIZ6Z.js} +864 -604
  4. package/dist/chunk-CQ2QIZ6Z.js.map +1 -0
  5. package/dist/{chunk-FSS45ZX3.js → chunk-DLYE6U2Z.js} +1516 -316
  6. package/dist/chunk-DLYE6U2Z.js.map +1 -0
  7. package/dist/{chunk-3NAWISI5.js → chunk-QFHCXJ2V.js} +348 -101
  8. package/dist/chunk-QFHCXJ2V.js.map +1 -0
  9. package/dist/chunk-XV52IHVR.js +977 -0
  10. package/dist/chunk-XV52IHVR.js.map +1 -0
  11. package/dist/daemon.js +3 -4
  12. package/dist/daemon.js.map +1 -1
  13. package/dist/e2e/run-journey.d.ts +1 -0
  14. package/dist/e2e/run-journey.js +19953 -0
  15. package/dist/e2e/run-journey.js.map +1 -0
  16. package/dist/{ed25519-2QVPINLS.js → ed25519-2LFQXLYS.js} +6 -2
  17. package/dist/index.d.ts +282 -68
  18. package/dist/index.js +380 -5
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp.js +39 -6
  21. package/dist/mcp.js.map +1 -1
  22. package/package.json +11 -7
  23. package/dist/anon-proxy-W3KMM7GU-FN7ZJY7P.js +0 -25
  24. package/dist/chunk-3NAWISI5.js.map +0 -1
  25. package/dist/chunk-5KFXUT5Q.js.map +0 -1
  26. package/dist/chunk-FSS45ZX3.js.map +0 -1
  27. package/dist/chunk-SKQTKZIH.js +0 -278
  28. package/dist/chunk-SKQTKZIH.js.map +0 -1
  29. package/dist/chunk-ZQKYZJWT.js +0 -359
  30. package/dist/chunk-ZQKYZJWT.js.map +0 -1
  31. package/dist/ed25519-2QVPINLS.js.map +0 -1
  32. package/dist/gateway-QOK47RKS-SEGTXBR3.js +0 -16
  33. package/dist/gateway-QOK47RKS-SEGTXBR3.js.map +0 -1
  34. package/dist/socks5-WTJBYGME-6COK4LXW.js +0 -139
  35. package/dist/socks5-WTJBYGME-6COK4LXW.js.map +0 -1
  36. /package/dist/{anon-proxy-W3KMM7GU-FN7ZJY7P.js.map → ed25519-2LFQXLYS.js.map} +0 -0
@@ -3,11 +3,13 @@ import {
3
3
  hmac
4
4
  } from "./chunk-32QD72IL.js";
5
5
  import {
6
- ed25519
7
- } from "./chunk-FSS45ZX3.js";
6
+ ed25519,
7
+ hexToBytes,
8
+ sha256
9
+ } from "./chunk-DLYE6U2Z.js";
8
10
  import {
9
11
  HashMD,
10
- sha256,
12
+ sha256 as sha2562,
11
13
  sha512
12
14
  } from "./chunk-LR7W2ISE.js";
13
15
  import {
@@ -20,7 +22,7 @@ import {
20
22
  concatBytes,
21
23
  createHasher,
22
24
  createView,
23
- hexToBytes,
25
+ hexToBytes as hexToBytes2,
24
26
  isBytes,
25
27
  kdfInputToBytes,
26
28
  randomBytes,
@@ -29,7 +31,7 @@ import {
29
31
  } from "./chunk-VA7XC4FD.js";
30
32
 
31
33
  // ../client/dist/index.js
32
- import { generateSecretKey as generateSecretKey3, getPublicKey as getPublicKey22 } from "nostr-tools/pure";
34
+ import { generateSecretKey as generateSecretKey3, getPublicKey as getPublicKey22, finalizeEvent as finalizeEvent11 } from "nostr-tools/pure";
33
35
  import { generateSecretKey as generateSecretKey2 } from "nostr-tools/pure";
34
36
 
35
37
  // ../../node_modules/.pnpm/@toon-protocol+core@1.4.2_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/chunk-5WT7ISKC.js
@@ -164,380 +166,13 @@ import { SimplePool as SimplePool2 } from "nostr-tools/pool";
164
166
  import { getPublicKey } from "nostr-tools/pure";
165
167
  import WebSocket2 from "ws";
166
168
  import { getPublicKey as getPublicKey2, verifyEvent } from "nostr-tools/pure";
167
-
168
- // ../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/utils.js
169
- function isBytes2(a) {
170
- return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
171
- }
172
- function abytes2(value, length, title = "") {
173
- const bytes = isBytes2(value);
174
- const len = value?.length;
175
- const needsLen = length !== void 0;
176
- if (!bytes || needsLen && len !== length) {
177
- const prefix = title && `"${title}" `;
178
- const ofLen = needsLen ? ` of length ${length}` : "";
179
- const got = bytes ? `length=${len}` : `type=${typeof value}`;
180
- const message = prefix + "expected Uint8Array" + ofLen + ", got " + got;
181
- if (!bytes)
182
- throw new TypeError(message);
183
- throw new RangeError(message);
184
- }
185
- return value;
186
- }
187
- function aexists(instance, checkFinished = true) {
188
- if (instance.destroyed)
189
- throw new Error("Hash instance has been destroyed");
190
- if (checkFinished && instance.finished)
191
- throw new Error("Hash#digest() has already been called");
192
- }
193
- function aoutput(out, instance) {
194
- abytes2(out, void 0, "digestInto() output");
195
- const min = instance.outputLen;
196
- if (out.length < min) {
197
- throw new RangeError('"digestInto() output" expected to be of length >=' + min);
198
- }
199
- }
200
- function clean2(...arrays) {
201
- for (let i = 0; i < arrays.length; i++) {
202
- arrays[i].fill(0);
203
- }
204
- }
205
- function createView2(arr) {
206
- return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
207
- }
208
- function rotr(word, shift) {
209
- return word << 32 - shift | word >>> shift;
210
- }
211
- var hasHexBuiltin = /* @__PURE__ */ (() => (
212
- // @ts-ignore
213
- typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
214
- ))();
215
- var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
216
- function asciiToBase16(ch) {
217
- if (ch >= asciis._0 && ch <= asciis._9)
218
- return ch - asciis._0;
219
- if (ch >= asciis.A && ch <= asciis.F)
220
- return ch - (asciis.A - 10);
221
- if (ch >= asciis.a && ch <= asciis.f)
222
- return ch - (asciis.a - 10);
223
- return;
224
- }
225
- function hexToBytes2(hex) {
226
- if (typeof hex !== "string")
227
- throw new TypeError("hex string expected, got " + typeof hex);
228
- if (hasHexBuiltin) {
229
- try {
230
- return Uint8Array.fromHex(hex);
231
- } catch (error) {
232
- if (error instanceof SyntaxError)
233
- throw new RangeError(error.message);
234
- throw error;
235
- }
236
- }
237
- const hl = hex.length;
238
- const al = hl / 2;
239
- if (hl % 2)
240
- throw new RangeError("hex string expected, got unpadded hex of length " + hl);
241
- const array = new Uint8Array(al);
242
- for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
243
- const n1 = asciiToBase16(hex.charCodeAt(hi));
244
- const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
245
- if (n1 === void 0 || n2 === void 0) {
246
- const char = hex[hi] + hex[hi + 1];
247
- throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi);
248
- }
249
- array[ai] = n1 * 16 + n2;
250
- }
251
- return array;
252
- }
253
- function createHasher2(hashCons, info = {}) {
254
- const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
255
- const tmp = hashCons(void 0);
256
- hashC.outputLen = tmp.outputLen;
257
- hashC.blockLen = tmp.blockLen;
258
- hashC.canXOF = tmp.canXOF;
259
- hashC.create = (opts) => hashCons(opts);
260
- Object.assign(hashC, info);
261
- return Object.freeze(hashC);
262
- }
263
- var oidNist = (suffix) => ({
264
- // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
265
- // Larger suffix values would need base-128 OID encoding and a different length byte.
266
- oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
267
- });
268
-
269
- // ../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/_md.js
270
- function Chi(a, b, c) {
271
- return a & b ^ ~a & c;
272
- }
273
- function Maj(a, b, c) {
274
- return a & b ^ a & c ^ b & c;
275
- }
276
- var HashMD2 = class {
277
- blockLen;
278
- outputLen;
279
- canXOF = false;
280
- padOffset;
281
- isLE;
282
- // For partial updates less than block size
283
- buffer;
284
- view;
285
- finished = false;
286
- length = 0;
287
- pos = 0;
288
- destroyed = false;
289
- constructor(blockLen, outputLen, padOffset, isLE) {
290
- this.blockLen = blockLen;
291
- this.outputLen = outputLen;
292
- this.padOffset = padOffset;
293
- this.isLE = isLE;
294
- this.buffer = new Uint8Array(blockLen);
295
- this.view = createView2(this.buffer);
296
- }
297
- update(data) {
298
- aexists(this);
299
- abytes2(data);
300
- const { view, buffer, blockLen } = this;
301
- const len = data.length;
302
- for (let pos = 0; pos < len; ) {
303
- const take = Math.min(blockLen - this.pos, len - pos);
304
- if (take === blockLen) {
305
- const dataView = createView2(data);
306
- for (; blockLen <= len - pos; pos += blockLen)
307
- this.process(dataView, pos);
308
- continue;
309
- }
310
- buffer.set(data.subarray(pos, pos + take), this.pos);
311
- this.pos += take;
312
- pos += take;
313
- if (this.pos === blockLen) {
314
- this.process(view, 0);
315
- this.pos = 0;
316
- }
317
- }
318
- this.length += data.length;
319
- this.roundClean();
320
- return this;
321
- }
322
- digestInto(out) {
323
- aexists(this);
324
- aoutput(out, this);
325
- this.finished = true;
326
- const { buffer, view, blockLen, isLE } = this;
327
- let { pos } = this;
328
- buffer[pos++] = 128;
329
- clean2(this.buffer.subarray(pos));
330
- if (this.padOffset > blockLen - pos) {
331
- this.process(view, 0);
332
- pos = 0;
333
- }
334
- for (let i = pos; i < blockLen; i++)
335
- buffer[i] = 0;
336
- view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
337
- this.process(view, 0);
338
- const oview = createView2(out);
339
- const len = this.outputLen;
340
- if (len % 4)
341
- throw new Error("_sha2: outputLen must be aligned to 32bit");
342
- const outLen = len / 4;
343
- const state = this.get();
344
- if (outLen > state.length)
345
- throw new Error("_sha2: outputLen bigger than state");
346
- for (let i = 0; i < outLen; i++)
347
- oview.setUint32(4 * i, state[i], isLE);
348
- }
349
- digest() {
350
- const { buffer, outputLen } = this;
351
- this.digestInto(buffer);
352
- const res = buffer.slice(0, outputLen);
353
- this.destroy();
354
- return res;
355
- }
356
- _cloneInto(to) {
357
- to ||= new this.constructor();
358
- to.set(...this.get());
359
- const { blockLen, buffer, length, finished, destroyed, pos } = this;
360
- to.destroyed = destroyed;
361
- to.finished = finished;
362
- to.length = length;
363
- to.pos = pos;
364
- if (length % blockLen)
365
- to.buffer.set(buffer);
366
- return to;
367
- }
368
- clone() {
369
- return this._cloneInto();
370
- }
371
- };
372
- var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
373
- 1779033703,
374
- 3144134277,
375
- 1013904242,
376
- 2773480762,
377
- 1359893119,
378
- 2600822924,
379
- 528734635,
380
- 1541459225
381
- ]);
382
-
383
- // ../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/sha2.js
384
- var SHA256_K = /* @__PURE__ */ Uint32Array.from([
385
- 1116352408,
386
- 1899447441,
387
- 3049323471,
388
- 3921009573,
389
- 961987163,
390
- 1508970993,
391
- 2453635748,
392
- 2870763221,
393
- 3624381080,
394
- 310598401,
395
- 607225278,
396
- 1426881987,
397
- 1925078388,
398
- 2162078206,
399
- 2614888103,
400
- 3248222580,
401
- 3835390401,
402
- 4022224774,
403
- 264347078,
404
- 604807628,
405
- 770255983,
406
- 1249150122,
407
- 1555081692,
408
- 1996064986,
409
- 2554220882,
410
- 2821834349,
411
- 2952996808,
412
- 3210313671,
413
- 3336571891,
414
- 3584528711,
415
- 113926993,
416
- 338241895,
417
- 666307205,
418
- 773529912,
419
- 1294757372,
420
- 1396182291,
421
- 1695183700,
422
- 1986661051,
423
- 2177026350,
424
- 2456956037,
425
- 2730485921,
426
- 2820302411,
427
- 3259730800,
428
- 3345764771,
429
- 3516065817,
430
- 3600352804,
431
- 4094571909,
432
- 275423344,
433
- 430227734,
434
- 506948616,
435
- 659060556,
436
- 883997877,
437
- 958139571,
438
- 1322822218,
439
- 1537002063,
440
- 1747873779,
441
- 1955562222,
442
- 2024104815,
443
- 2227730452,
444
- 2361852424,
445
- 2428436474,
446
- 2756734187,
447
- 3204031479,
448
- 3329325298
449
- ]);
450
- var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
451
- var SHA2_32B = class extends HashMD2 {
452
- constructor(outputLen) {
453
- super(64, outputLen, 8, false);
454
- }
455
- get() {
456
- const { A, B, C, D, E, F, G, H } = this;
457
- return [A, B, C, D, E, F, G, H];
458
- }
459
- // prettier-ignore
460
- set(A, B, C, D, E, F, G, H) {
461
- this.A = A | 0;
462
- this.B = B | 0;
463
- this.C = C | 0;
464
- this.D = D | 0;
465
- this.E = E | 0;
466
- this.F = F | 0;
467
- this.G = G | 0;
468
- this.H = H | 0;
469
- }
470
- process(view, offset) {
471
- for (let i = 0; i < 16; i++, offset += 4)
472
- SHA256_W[i] = view.getUint32(offset, false);
473
- for (let i = 16; i < 64; i++) {
474
- const W15 = SHA256_W[i - 15];
475
- const W2 = SHA256_W[i - 2];
476
- const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
477
- const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
478
- SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
479
- }
480
- let { A, B, C, D, E, F, G, H } = this;
481
- for (let i = 0; i < 64; i++) {
482
- const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
483
- const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
484
- const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
485
- const T2 = sigma0 + Maj(A, B, C) | 0;
486
- H = G;
487
- G = F;
488
- F = E;
489
- E = D + T1 | 0;
490
- D = C;
491
- C = B;
492
- B = A;
493
- A = T1 + T2 | 0;
494
- }
495
- A = A + this.A | 0;
496
- B = B + this.B | 0;
497
- C = C + this.C | 0;
498
- D = D + this.D | 0;
499
- E = E + this.E | 0;
500
- F = F + this.F | 0;
501
- G = G + this.G | 0;
502
- H = H + this.H | 0;
503
- this.set(A, B, C, D, E, F, G, H);
504
- }
505
- roundClean() {
506
- clean2(SHA256_W);
507
- }
508
- destroy() {
509
- this.destroyed = true;
510
- this.set(0, 0, 0, 0, 0, 0, 0, 0);
511
- clean2(this.buffer);
512
- }
513
- };
514
- var _SHA256 = class extends SHA2_32B {
515
- // We cannot use array here since array allows indexing by variable
516
- // which means optimizer/compiler cannot use registers.
517
- A = SHA256_IV[0] | 0;
518
- B = SHA256_IV[1] | 0;
519
- C = SHA256_IV[2] | 0;
520
- D = SHA256_IV[3] | 0;
521
- E = SHA256_IV[4] | 0;
522
- F = SHA256_IV[5] | 0;
523
- G = SHA256_IV[6] | 0;
524
- H = SHA256_IV[7] | 0;
525
- constructor() {
526
- super(32);
527
- }
528
- };
529
- var sha2562 = /* @__PURE__ */ createHasher2(
530
- () => new _SHA256(),
531
- /* @__PURE__ */ oidNist(1)
532
- );
533
-
534
- // ../../node_modules/.pnpm/@toon-protocol+core@1.4.2_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/index.js
535
169
  import { SimplePool as SimplePool3 } from "nostr-tools/pool";
536
170
  import { getPublicKey as getPublicKey3 } from "nostr-tools/pure";
537
171
  import WebSocket22 from "ws";
538
172
  import { getPublicKey as getPublicKey4 } from "nostr-tools/pure";
539
173
  import { getPublicKey as getPublicKey5 } from "nostr-tools/pure";
540
174
  var ILP_PEER_INFO_KIND = 10032;
175
+ var BLOB_STORAGE_REQUEST_KIND = 5094;
541
176
  var ILP_SEGMENT_PATTERN = /^[a-z0-9-]+$/;
542
177
  var MAX_ILP_ADDRESS_LENGTH = 1023;
543
178
  function isValidIlpAddressStructure(address) {
@@ -920,6 +555,40 @@ function buildIlpPeerInfoEvent(info, secretKey, options = {}) {
920
555
  secretKey
921
556
  );
922
557
  }
558
+ function buildBlobStorageRequest(params, secretKey) {
559
+ if (!params.blobData || params.blobData.length === 0) {
560
+ throw new ToonError(
561
+ "Blob storage request blobData is required and must not be empty",
562
+ "DVM_MISSING_INPUT"
563
+ );
564
+ }
565
+ if (typeof params.bid !== "string" || params.bid === "") {
566
+ throw new ToonError(
567
+ "Blob storage request bid must be a non-empty string (USDC micro-units)",
568
+ "DVM_INVALID_BID"
569
+ );
570
+ }
571
+ const contentType = params.contentType ?? "application/octet-stream";
572
+ const base64Blob = params.blobData.toString("base64");
573
+ const tags = [];
574
+ tags.push(["i", base64Blob, "blob"]);
575
+ tags.push(["bid", params.bid, "usdc"]);
576
+ tags.push(["output", contentType]);
577
+ if (params.params !== void 0) {
578
+ for (const p of params.params) {
579
+ tags.push(["param", p.key, p.value]);
580
+ }
581
+ }
582
+ return finalizeEvent9(
583
+ {
584
+ kind: BLOB_STORAGE_REQUEST_KIND,
585
+ content: "",
586
+ tags,
587
+ created_at: Math.floor(Date.now() / 1e3)
588
+ },
589
+ secretKey
590
+ );
591
+ }
923
592
  var genesis_peers_default = [];
924
593
  var PUBKEY_REGEX3 = /^[0-9a-f]{64}$/;
925
594
  var ILP_ADDRESS_REGEX = /^g\.[a-zA-Z0-9.-]+$/;
@@ -1132,11 +801,11 @@ function resolveTokenForChain(chain2, requesterPreferredTokens, responderPreferr
1132
801
  return void 0;
1133
802
  }
1134
803
  function hexToBytes3(hex) {
1135
- const clean3 = hex.startsWith("0x") ? hex.slice(2) : hex;
1136
- if (clean3.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(clean3)) {
804
+ const clean2 = hex.startsWith("0x") ? hex.slice(2) : hex;
805
+ if (clean2.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(clean2)) {
1137
806
  throw new Error(`Invalid hex string: ${hex}`);
1138
807
  }
1139
- return hexToBytes2(clean3);
808
+ return hexToBytes(clean2);
1140
809
  }
1141
810
  function concatBytes2(...parts) {
1142
811
  let len = 0;
@@ -1197,7 +866,7 @@ function hexToMinaBase58PrivateKey(privateKey) {
1197
866
  Uint8Array.from([MINA_PRIVATE_KEY_VERSION, 1]),
1198
867
  leScalar
1199
868
  );
1200
- const checksum2 = sha2562(sha2562(payload)).slice(0, 4);
869
+ const checksum2 = sha256(sha256(payload)).slice(0, 4);
1201
870
  return base58Encode(concatBytes2(payload, checksum2));
1202
871
  }
1203
872
  var BootstrapError = class extends ToonError {
@@ -2343,7 +2012,7 @@ function pbkdf2(hash, password, salt, opts) {
2343
2012
  }
2344
2013
 
2345
2014
  // ../../node_modules/.pnpm/@scure+base@1.2.6/node_modules/@scure/base/lib/esm/index.js
2346
- function isBytes3(a) {
2015
+ function isBytes2(a) {
2347
2016
  return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
2348
2017
  }
2349
2018
  function isArrayOf(isString, arr) {
@@ -2558,7 +2227,7 @@ function radix(num) {
2558
2227
  const _256 = 2 ** 8;
2559
2228
  return {
2560
2229
  encode: (bytes) => {
2561
- if (!isBytes3(bytes))
2230
+ if (!isBytes2(bytes))
2562
2231
  throw new Error("radix.encode input should be Uint8Array");
2563
2232
  return convertRadix(Array.from(bytes), _256, num);
2564
2233
  },
@@ -2577,7 +2246,7 @@ function radix2(bits, revPadding = false) {
2577
2246
  throw new Error("radix2: carry overflow");
2578
2247
  return {
2579
2248
  encode: (bytes) => {
2580
- if (!isBytes3(bytes))
2249
+ if (!isBytes2(bytes))
2581
2250
  throw new Error("radix2.encode input should be Uint8Array");
2582
2251
  return convertRadix2(Array.from(bytes), 8, bits, !revPadding);
2583
2252
  },
@@ -2592,7 +2261,7 @@ function checksum(len, fn) {
2592
2261
  afn(fn);
2593
2262
  return {
2594
2263
  encode(data) {
2595
- if (!isBytes3(data))
2264
+ if (!isBytes2(data))
2596
2265
  throw new Error("checksum.encode: input should be Uint8Array");
2597
2266
  const sum = fn(data).slice(0, len);
2598
2267
  const res = new Uint8Array(data.length + len);
@@ -2601,7 +2270,7 @@ function checksum(len, fn) {
2601
2270
  return res;
2602
2271
  },
2603
2272
  decode(data) {
2604
- if (!isBytes3(data))
2273
+ if (!isBytes2(data))
2605
2274
  throw new Error("checksum.decode: input should be Uint8Array");
2606
2275
  const payload = data.slice(0, -len);
2607
2276
  const oldChecksum = data.slice(-len);
@@ -2653,7 +2322,7 @@ function generateMnemonic(wordlist2, strength = 128) {
2653
2322
  }
2654
2323
  var calcChecksum = (entropy) => {
2655
2324
  const bitsLeft = 8 - entropy.length / 4;
2656
- return new Uint8Array([sha256(entropy)[0] >> bitsLeft << bitsLeft]);
2325
+ return new Uint8Array([sha2562(entropy)[0] >> bitsLeft << bitsLeft]);
2657
2326
  };
2658
2327
  function getCoder(wordlist2) {
2659
2328
  if (!Array.isArray(wordlist2) || wordlist2.length !== 2048 || typeof wordlist2[0] !== "string")
@@ -4777,7 +4446,7 @@ function bytesToNumberLE(bytes) {
4777
4446
  return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
4778
4447
  }
4779
4448
  function numberToBytesBE(n, len) {
4780
- return hexToBytes(n.toString(16).padStart(len * 2, "0"));
4449
+ return hexToBytes2(n.toString(16).padStart(len * 2, "0"));
4781
4450
  }
4782
4451
  function numberToBytesLE(n, len) {
4783
4452
  return numberToBytesBE(n, len).reverse();
@@ -4786,7 +4455,7 @@ function ensureBytes(title, hex, expectedLength) {
4786
4455
  let res;
4787
4456
  if (typeof hex === "string") {
4788
4457
  try {
4789
- res = hexToBytes(hex);
4458
+ res = hexToBytes2(hex);
4790
4459
  } catch (e) {
4791
4460
  throw new Error(title + " must be hex string or Uint8Array, cause: " + e);
4792
4461
  }
@@ -6266,7 +5935,7 @@ function ecdsa(Point2, hash, ecdsaOpts = {}) {
6266
5935
  return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
6267
5936
  }
6268
5937
  static fromHex(hex, format) {
6269
- return this.fromBytes(hexToBytes(hex), format);
5938
+ return this.fromBytes(hexToBytes2(hex), format);
6270
5939
  }
6271
5940
  addRecoveryBit(recovery) {
6272
5941
  return new Signature(this.r, this.s, recovery);
@@ -6301,7 +5970,7 @@ function ecdsa(Point2, hash, ecdsaOpts = {}) {
6301
5970
  toBytes(format = defaultSigOpts_format) {
6302
5971
  validateSigFormat(format);
6303
5972
  if (format === "der")
6304
- return hexToBytes(DER.hexFromSig(this));
5973
+ return hexToBytes2(DER.hexFromSig(this));
6305
5974
  const r = Fn.toBytes(this.r);
6306
5975
  const s = Fn.toBytes(this.s);
6307
5976
  if (format === "recovered") {
@@ -6575,7 +6244,7 @@ function sqrtMod(y) {
6575
6244
  return root;
6576
6245
  }
6577
6246
  var Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod });
6578
- var secp256k1 = createCurve({ ...secp256k1_CURVE, Fp: Fpk1, lowS: true, endo: secp256k1_ENDO }, sha256);
6247
+ var secp256k1 = createCurve({ ...secp256k1_CURVE, Fp: Fpk1, lowS: true, endo: secp256k1_ENDO }, sha2562);
6579
6248
 
6580
6249
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/legacy.js
6581
6250
  var Rho160 = /* @__PURE__ */ Uint8Array.from([
@@ -6697,7 +6366,7 @@ var ripemd160 = /* @__PURE__ */ createHasher(() => new RIPEMD160());
6697
6366
 
6698
6367
  // ../../node_modules/.pnpm/@scure+bip32@1.7.0/node_modules/@scure/bip32/lib/esm/index.js
6699
6368
  var Point = secp256k1.ProjectivePoint;
6700
- var base58check = createBase58check(sha256);
6369
+ var base58check = createBase58check(sha2562);
6701
6370
  function bytesToNumber(bytes) {
6702
6371
  abytes(bytes);
6703
6372
  const h = bytes.length === 0 ? "0" : bytesToHex(bytes);
@@ -6706,12 +6375,12 @@ function bytesToNumber(bytes) {
6706
6375
  function numberToBytes(num) {
6707
6376
  if (typeof num !== "bigint")
6708
6377
  throw new Error("bigint expected");
6709
- return hexToBytes(num.toString(16).padStart(64, "0"));
6378
+ return hexToBytes2(num.toString(16).padStart(64, "0"));
6710
6379
  }
6711
6380
  var MASTER_SECRET = utf8ToBytes("Bitcoin seed");
6712
6381
  var BITCOIN_VERSIONS = { private: 76066276, public: 76067358 };
6713
6382
  var HARDENED_OFFSET = 2147483648;
6714
- var hash160 = (data) => ripemd160(sha256(data));
6383
+ var hash160 = (data) => ripemd160(sha2562(data));
6715
6384
  var fromU32 = (data) => createView(data).getUint32(0, false);
6716
6385
  var toU32 = (n) => {
6717
6386
  if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) {
@@ -6952,7 +6621,7 @@ import {
6952
6621
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
6953
6622
  import { toHex as toHex3 } from "viem";
6954
6623
  import { readFileSync, writeFileSync, existsSync } from "fs";
6955
- import { finalizeEvent as finalizeEvent11 } from "nostr-tools/pure";
6624
+ import { finalizeEvent as finalizeEvent22 } from "nostr-tools/pure";
6956
6625
  import { nip19 } from "nostr-tools";
6957
6626
  import { getPublicKey as getPublicKey32 } from "nostr-tools/pure";
6958
6627
  import {
@@ -6968,6 +6637,7 @@ var ToonClientError = class extends Error {
6968
6637
  this.code = code;
6969
6638
  this.name = "ToonClientError";
6970
6639
  }
6640
+ code;
6971
6641
  };
6972
6642
  var NetworkError = class extends ToonClientError {
6973
6643
  constructor(message, cause) {
@@ -7021,7 +6691,7 @@ function deriveEvmIdentity(secretKey) {
7021
6691
  async function deriveSolanaKey(seed, accountIndex = 0) {
7022
6692
  const { hmac: hmac2 } = await import("./hmac-26UC6YKX.js");
7023
6693
  const { sha512: sha5122 } = await import("./sha512-WYC446ZM.js");
7024
- const { ed25519: ed255194 } = await import("./ed25519-2QVPINLS.js");
6694
+ const { ed25519: ed255194 } = await import("./ed25519-2LFQXLYS.js");
7025
6695
  const encoder = new TextEncoder();
7026
6696
  let I = hmac2(sha5122, encoder.encode("ed25519 seed"), seed);
7027
6697
  let key = I.slice(0, 32);
@@ -7161,26 +6831,49 @@ function getNetworkStatus(config) {
7161
6831
  if (!network || network === "custom") return void 0;
7162
6832
  return resolveClientNetwork(network).status;
7163
6833
  }
6834
+ function proxyIlpEndpoint(proxyUrl) {
6835
+ if (!proxyUrl) return void 0;
6836
+ const trimmed = proxyUrl.replace(/\/+$/, "");
6837
+ return /\/ilp$/i.test(trimmed) ? trimmed : `${trimmed}/ilp`;
6838
+ }
7164
6839
  function validateConfig(config) {
7165
6840
  if (config.connector !== void 0) {
7166
6841
  throw new ValidationError(
7167
6842
  "Embedded mode not yet implemented in ToonClient. Use connectorUrl for HTTP mode."
7168
6843
  );
7169
6844
  }
7170
- if (!config.connectorUrl) {
6845
+ if (!config.connectorUrl && !config.proxyUrl) {
7171
6846
  throw new ValidationError(
7172
- 'connectorUrl is required for HTTP mode. Example: "http://localhost:8080"'
6847
+ 'connectorUrl (or proxyUrl) is required for HTTP mode. Example: "http://localhost:8080"'
7173
6848
  );
7174
6849
  }
7175
- try {
7176
- const url = new URL(config.connectorUrl);
7177
- if (!url.protocol.startsWith("http")) {
7178
- throw new Error("Must be HTTP or HTTPS");
6850
+ if (config.connectorUrl) {
6851
+ try {
6852
+ const url = new URL(config.connectorUrl);
6853
+ if (!url.protocol.startsWith("http")) {
6854
+ throw new Error("Must be HTTP or HTTPS");
6855
+ }
6856
+ } catch (error) {
6857
+ throw new ValidationError(
6858
+ `Invalid connectorUrl: must be a valid HTTP/HTTPS URL (e.g., "http://localhost:8080"). Error: ${error instanceof Error ? error.message : String(error)}`
6859
+ );
6860
+ }
6861
+ }
6862
+ for (const [field, value] of [
6863
+ ["proxyUrl", config.proxyUrl],
6864
+ ["faucetUrl", config.faucetUrl]
6865
+ ]) {
6866
+ if (value === void 0) continue;
6867
+ try {
6868
+ const url = new URL(value);
6869
+ if (!url.protocol.startsWith("http")) {
6870
+ throw new Error("Must be HTTP or HTTPS");
6871
+ }
6872
+ } catch (error) {
6873
+ throw new ValidationError(
6874
+ `Invalid ${field}: must be a valid HTTP/HTTPS URL. Error: ${error instanceof Error ? error.message : String(error)}`
6875
+ );
7179
6876
  }
7180
- } catch (error) {
7181
- throw new ValidationError(
7182
- `Invalid connectorUrl: must be a valid HTTP/HTTPS URL (e.g., "http://localhost:8080"). Error: ${error instanceof Error ? error.message : String(error)}`
7183
- );
7184
6877
  }
7185
6878
  if (config.secretKey !== void 0) {
7186
6879
  if (!config.secretKey || config.secretKey.length !== 32) {
@@ -7244,25 +6937,6 @@ function validateConfig(config) {
7244
6937
  );
7245
6938
  }
7246
6939
  }
7247
- if (config.transport) {
7248
- if (config.transport.type === "socks5") {
7249
- if (!config.transport.socksProxy?.startsWith("socks5h://")) {
7250
- throw new ValidationError(
7251
- 'transport.socksProxy must use socks5h:// scheme to prevent DNS leaks. The "h" suffix ensures .anyone hostnames are resolved by the proxy, not locally.'
7252
- );
7253
- }
7254
- } else if (config.transport.type === "gateway") {
7255
- if (!config.transport.gatewayUrl) {
7256
- throw new ValidationError(
7257
- "transport.gatewayUrl is required for gateway transport"
7258
- );
7259
- }
7260
- } else if (config.transport.type !== "direct") {
7261
- throw new ValidationError(
7262
- `Unknown transport type: "${config.transport.type}"`
7263
- );
7264
- }
7265
- }
7266
6940
  if (config.chainRpcUrls && config.supportedChains) {
7267
6941
  for (const chain2 of Object.keys(config.chainRpcUrls)) {
7268
6942
  if (!config.supportedChains.includes(chain2)) {
@@ -7279,19 +6953,21 @@ function applyDefaults(rawConfig) {
7279
6953
  config.mnemonic,
7280
6954
  config.mnemonicAccountIndex ?? 0
7281
6955
  ).secretKey : generateSecretKey2());
6956
+ const connectorHttpEndpoint = config.connectorHttpEndpoint ?? proxyIlpEndpoint(config.proxyUrl);
6957
+ const connectorUrl = config.connectorUrl ?? config.proxyUrl?.replace(/\/+$/, "");
7282
6958
  let btpUrl = config.btpUrl;
7283
- if (!btpUrl && config.connectorUrl) {
6959
+ if (!btpUrl && connectorUrl && !connectorHttpEndpoint) {
7284
6960
  try {
7285
- const url = new URL(config.connectorUrl);
6961
+ const url = new URL(connectorUrl);
7286
6962
  const wsProtocol = url.protocol === "https:" ? "wss:" : "ws:";
7287
6963
  btpUrl = `${wsProtocol}//${url.hostname}:3000`;
7288
6964
  } catch {
7289
6965
  }
7290
6966
  }
7291
6967
  let destinationAddress = config.destinationAddress;
7292
- if (!destinationAddress && config.connectorUrl) {
6968
+ if (!destinationAddress && connectorUrl) {
7293
6969
  try {
7294
- const url = new URL(config.connectorUrl);
6970
+ const url = new URL(connectorUrl);
7295
6971
  if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
7296
6972
  if (url.port === "8080") {
7297
6973
  destinationAddress = "g.toon.genesis";
@@ -7314,8 +6990,10 @@ function applyDefaults(rawConfig) {
7314
6990
  ...config,
7315
6991
  secretKey,
7316
6992
  evmPrivateKey,
7317
- connectorUrl: config.connectorUrl,
7318
- // Already validated as required
6993
+ // Satisfied by connectorUrl OR proxyUrl (validateConfig enforced one of them).
6994
+ connectorUrl,
6995
+ // Surface the derived `POST /ilp` endpoint so HTTP mode selects HttpIlpClient.
6996
+ ...connectorHttpEndpoint ? { connectorHttpEndpoint } : {},
7319
6997
  relayUrl: config.relayUrl ?? "ws://localhost:7100",
7320
6998
  queryTimeout: config.queryTimeout ?? 3e4,
7321
6999
  maxRetries: config.maxRetries ?? 3,
@@ -7369,9 +7047,19 @@ function toHex2(bytes) {
7369
7047
  function encodeUtf8(str) {
7370
7048
  return new TextEncoder().encode(str);
7371
7049
  }
7050
+ function decodeUtf8(bytes) {
7051
+ return new TextDecoder().decode(bytes);
7052
+ }
7372
7053
  function isBase64(str) {
7373
7054
  return /^[A-Za-z0-9+/]*={0,2}$/.test(str);
7374
7055
  }
7056
+ var REQUEST_LINE = "POST /write HTTP/1.1";
7057
+ var HEADERS = ["Host: relay", "Content-Type: application/json"];
7058
+ function buildStoreWriteEnvelope(event) {
7059
+ const body = JSON.stringify({ event });
7060
+ const head = [REQUEST_LINE, ...HEADERS].join("\r\n");
7061
+ return encodeUtf8(head + "\r\n\r\n" + body);
7062
+ }
7375
7063
  async function withRetry(operation, options) {
7376
7064
  const {
7377
7065
  maxRetries,
@@ -8232,6 +7920,227 @@ var BtpRuntimeClient = class {
8232
7920
  };
8233
7921
  }
8234
7922
  };
7923
+ var ILP_CLAIM_HEADER = "ILP-Payment-Channel-Claim";
7924
+ var ILP_PEER_ID_HEADER = "ILP-Peer-Id";
7925
+ var HttpIlpClient = class {
7926
+ httpEndpoint;
7927
+ peerId;
7928
+ authToken;
7929
+ timeout;
7930
+ retryConfig;
7931
+ httpClient;
7932
+ createWebSocket;
7933
+ constructor(config) {
7934
+ this.httpEndpoint = config.httpEndpoint;
7935
+ this.peerId = config.peerId;
7936
+ this.authToken = config.authToken;
7937
+ this.timeout = config.timeout ?? 3e4;
7938
+ this.retryConfig = {
7939
+ maxRetries: config.maxRetries ?? 3,
7940
+ retryDelay: config.retryDelay ?? 1e3
7941
+ };
7942
+ this.httpClient = config.httpClient ?? fetch;
7943
+ this.createWebSocket = config.createWebSocket;
7944
+ }
7945
+ /**
7946
+ * Send an ILP PREPARE via `POST /ilp` WITHOUT a claim. The connector accepts
7947
+ * this only on free/zero-amount routes; paid writes must use
7948
+ * {@link sendIlpPacketWithClaim}. Satisfies the IlpClient interface.
7949
+ */
7950
+ async sendIlpPacket(params) {
7951
+ return withRetry(() => this.postPrepare(params), {
7952
+ maxRetries: this.retryConfig.maxRetries,
7953
+ retryDelay: this.retryConfig.retryDelay,
7954
+ exponentialBackoff: true,
7955
+ shouldRetry: (error) => error instanceof NetworkError
7956
+ });
7957
+ }
7958
+ /**
7959
+ * Send an ILP PREPARE via `POST /ilp` with the payment-channel claim attached
7960
+ * as the `ILP-Payment-Channel-Claim` header. `claim` is the SAME JSON object
7961
+ * the BTP path attaches as the `payment-channel-claim` protocolData entry —
7962
+ * we base64(JSON.stringify(claim)) it, byte-for-byte identical to BTP.
7963
+ */
7964
+ async sendIlpPacketWithClaim(params, claim) {
7965
+ return withRetry(() => this.postPrepare(params, claim), {
7966
+ maxRetries: this.retryConfig.maxRetries,
7967
+ retryDelay: this.retryConfig.retryDelay,
7968
+ exponentialBackoff: true,
7969
+ shouldRetry: (error) => error instanceof NetworkError
7970
+ });
7971
+ }
7972
+ /**
7973
+ * Upgrade to a duplex BTP session over the SAME endpoint.
7974
+ *
7975
+ * Derives the `ws(s)://` URL from `httpEndpoint`, opens a WebSocket with
7976
+ * `Sec-WebSocket-Protocol: btp` and the same `ILP-Peer-Id` + `Authorization`
7977
+ * headers, and returns a connected {@link BtpRuntimeClient}. When auth headers
7978
+ * are present the connector pre-authenticates the session (no in-band auth
7979
+ * frame); without them the BtpRuntimeClient falls back to the normal BTP
7980
+ * auth-frame flow.
7981
+ *
7982
+ * NOTE: passing per-connection headers + a subprotocol to a WebSocket is
7983
+ * Node-only (the `ws` package). Browsers cannot set arbitrary request headers
7984
+ * on a WebSocket handshake, so a browser consumer must use the gateway
7985
+ * transport or BTP-with-auth-frame instead.
7986
+ */
7987
+ async upgradeToBtp() {
7988
+ const btpUrl = httpEndpointToBtpUrl(this.httpEndpoint);
7989
+ const createWebSocket = this.createWebSocket ?? await makeBtpWebSocketFactory(this.authHeaders());
7990
+ const client = new BtpRuntimeClient({
7991
+ btpUrl,
7992
+ // BtpRuntimeClient sends an auth frame using these; when the connector
7993
+ // pre-authenticated via Upgrade headers it accepts the (redundant) frame.
7994
+ peerId: this.peerId ?? "client",
7995
+ authToken: this.authToken ?? "",
7996
+ createWebSocket
7997
+ });
7998
+ await client.connect();
7999
+ return client;
8000
+ }
8001
+ // ─── Private ──────────────────────────────────────────────────────────────
8002
+ authHeaders() {
8003
+ const headers = {};
8004
+ if (this.peerId) headers[ILP_PEER_ID_HEADER] = this.peerId;
8005
+ if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
8006
+ return headers;
8007
+ }
8008
+ /**
8009
+ * Single attempt: serialize the PREPARE, POST it, and map the response.
8010
+ * @throws {NetworkError} On connection/timeout failures (retried).
8011
+ * @throws {ConnectorError} On non-retryable transport errors (5xx / unexpected).
8012
+ */
8013
+ async postPrepare(params, claim) {
8014
+ const requestTimeout = params.timeout ?? this.timeout;
8015
+ const prepare = serializeIlpPrepare({
8016
+ type: ILPPacketType.PREPARE,
8017
+ amount: BigInt(params.amount),
8018
+ destination: params.destination,
8019
+ executionCondition: new Uint8Array(32),
8020
+ expiresAt: new Date(Date.now() + requestTimeout),
8021
+ data: fromBase64(params.data)
8022
+ });
8023
+ const headers = {
8024
+ "Content-Type": "application/octet-stream",
8025
+ ...this.authHeaders()
8026
+ };
8027
+ if (claim !== void 0) {
8028
+ headers[ILP_CLAIM_HEADER] = toBase64(
8029
+ encodeUtf8(JSON.stringify(claim))
8030
+ );
8031
+ }
8032
+ const controller = new AbortController();
8033
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
8034
+ try {
8035
+ const response = await this.httpClient(this.httpEndpoint, {
8036
+ method: "POST",
8037
+ headers,
8038
+ // Copy into a fresh ArrayBuffer so fetch sees a clean body, not a view.
8039
+ body: prepare.slice(),
8040
+ signal: controller.signal
8041
+ });
8042
+ clearTimeout(timeoutId);
8043
+ return await this.mapResponse(response);
8044
+ } catch (error) {
8045
+ clearTimeout(timeoutId);
8046
+ throw this.mapTransportError(error, requestTimeout);
8047
+ }
8048
+ }
8049
+ /**
8050
+ * Map a `200 OK` body (OER FULFILL/REJECT) to an IlpSendResult; map a non-2xx
8051
+ * to a transport error. Per the wire contract, ILP-level rejects arrive as a
8052
+ * 200 + REJECT body — only HTTP non-2xx means a transport-layer failure.
8053
+ */
8054
+ async mapResponse(response) {
8055
+ if (response.ok) {
8056
+ const buf = new Uint8Array(await response.arrayBuffer());
8057
+ if (buf.length === 0) {
8058
+ throw new ConnectorError("Empty 200 body from /ilp (expected OER ILP response)");
8059
+ }
8060
+ const ilp = deserializeIlpPacket(buf);
8061
+ if (ilp.type === ILPPacketType.FULFILL) {
8062
+ return {
8063
+ accepted: true,
8064
+ data: ilp.data.length > 0 ? toBase64(ilp.data) : void 0
8065
+ };
8066
+ }
8067
+ return {
8068
+ accepted: false,
8069
+ code: ilp.code,
8070
+ message: ilp.message,
8071
+ data: ilp.data.length > 0 ? toBase64(ilp.data) : void 0
8072
+ };
8073
+ }
8074
+ const body = await response.text().catch(() => "");
8075
+ const detail = body ? `: ${body}` : "";
8076
+ if (response.status >= 500) {
8077
+ throw new ConnectorError(
8078
+ `Connector transport error (${response.status} ${response.statusText})${detail}`
8079
+ );
8080
+ }
8081
+ throw new ConnectorError(
8082
+ `ILP-over-HTTP request rejected (${response.status} ${response.statusText})${detail}`
8083
+ );
8084
+ }
8085
+ mapTransportError(error, requestTimeout) {
8086
+ if (error instanceof ConnectorError || error instanceof NetworkError) {
8087
+ return error;
8088
+ }
8089
+ if (error instanceof Error && error.name === "AbortError") {
8090
+ return new NetworkError(`Request timeout after ${requestTimeout}ms`, error);
8091
+ }
8092
+ if (error instanceof TypeError && (error.message.includes("fetch failed") || error.message.includes("ECONNREFUSED") || error.message.includes("ECONNRESET") || error.message.includes("ETIMEDOUT") || error.message.includes("network"))) {
8093
+ return new NetworkError(`Network connection failed: ${error.message}`, error);
8094
+ }
8095
+ return new ConnectorError(
8096
+ `Unexpected error during ILP-over-HTTP request: ${error instanceof Error ? error.message : String(error)}`,
8097
+ error instanceof Error ? error : void 0
8098
+ );
8099
+ }
8100
+ };
8101
+ function httpEndpointToBtpUrl(httpEndpoint) {
8102
+ return httpEndpoint.replace(/^https:\/\//i, "wss://").replace(/^http:\/\//i, "ws://");
8103
+ }
8104
+ async function makeBtpWebSocketFactory(headers) {
8105
+ const { createRequire } = await import("module");
8106
+ const require2 = createRequire(import.meta.url);
8107
+ const WS = require2("ws");
8108
+ const ws = WS;
8109
+ const WSClass = typeof ws === "function" ? ws : typeof ws.default === "function" ? ws.default : typeof ws.WebSocket === "function" ? ws.WebSocket : null;
8110
+ if (WSClass === null) {
8111
+ throw new Error(
8112
+ "makeBtpWebSocketFactory: require('ws') did not yield a constructor on .default, .WebSocket, or the module root."
8113
+ );
8114
+ }
8115
+ return (url) => (
8116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8117
+ new WSClass(url, "btp", { headers })
8118
+ );
8119
+ }
8120
+ function readDiscoveredIlpPeer(peer) {
8121
+ const p = peer ?? {};
8122
+ return {
8123
+ btpEndpoint: typeof p["btpEndpoint"] === "string" ? p["btpEndpoint"] : void 0,
8124
+ httpEndpoint: typeof p["httpEndpoint"] === "string" ? p["httpEndpoint"] : void 0,
8125
+ supportsUpgrade: typeof p["supportsUpgrade"] === "boolean" ? p["supportsUpgrade"] : void 0
8126
+ };
8127
+ }
8128
+ function selectIlpTransport(peer, options = {}) {
8129
+ const needsDuplex = options.needsDuplex ?? false;
8130
+ const http2 = peer.httpEndpoint?.trim() || void 0;
8131
+ const btp = peer.btpEndpoint?.trim() || void 0;
8132
+ const canUpgrade = peer.supportsUpgrade === true;
8133
+ if (needsDuplex) {
8134
+ if (btp) return { kind: "btp", btpEndpoint: btp };
8135
+ if (http2 && canUpgrade) return { kind: "http-upgradable", httpEndpoint: http2 };
8136
+ throw new Error(
8137
+ "Duplex transport required but peer exposes neither a btpEndpoint nor an upgradable httpEndpoint"
8138
+ );
8139
+ }
8140
+ if (http2) return { kind: "http", httpEndpoint: http2, canUpgrade };
8141
+ if (btp) return { kind: "btp", btpEndpoint: btp };
8142
+ throw new Error("Peer exposes neither an httpEndpoint nor a btpEndpoint");
8143
+ }
8235
8144
  var TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
8236
8145
  var SYSTEM_PROGRAM_ID = "11111111111111111111111111111111";
8237
8146
  var RENT_SYSVAR_ID = "SysvarRent111111111111111111111111111111111";
@@ -8319,7 +8228,7 @@ function findProgramAddress(seeds, programId) {
8319
8228
  input.set(programId, offset);
8320
8229
  offset += programId.length;
8321
8230
  input.set(PDA_MARKER, offset);
8322
- const hash = sha256(input);
8231
+ const hash = sha2562(input);
8323
8232
  if (!isOnCurve(hash)) return { pda: hash, bump };
8324
8233
  }
8325
8234
  throw new Error("Could not find a viable PDA bump seed");
@@ -9325,96 +9234,41 @@ var EvmSigner = class {
9325
9234
  };
9326
9235
  }
9327
9236
  };
9328
- function isAnyoneHost(url) {
9329
- if (!url) return false;
9330
- try {
9331
- const withScheme = /:\/\//.test(url) ? url : `ws://${url}`;
9332
- const host = new URL(withScheme).hostname.toLowerCase();
9333
- return host.endsWith(".anyone");
9334
- } catch {
9335
- return false;
9336
- }
9337
- }
9338
- async function resolveTransport(transport, originalBtpUrl, originalConnectorUrl, managedProxyOptions) {
9339
- const hasExplicitProxy = !!transport && (transport.type === "socks5" || transport.type === "gateway");
9340
- const envProxy = process.env["ANYONE_PROXY_URLS"];
9341
- if (!hasExplicitProxy && managedProxyOptions?.managedAnonProxy !== false && !envProxy && isAnyoneHost(originalBtpUrl)) {
9342
- const { startManagedAnonProxy: startManagedAnonProxy2 } = await import("./anon-proxy-W3KMM7GU-FN7ZJY7P.js");
9343
- const { createSocks5WebSocketFactory, createSocks5Fetch } = await import("./socks5-WTJBYGME-6COK4LXW.js");
9344
- const proxy = await startManagedAnonProxy2({
9345
- ...managedProxyOptions?.managedAnonSocksPort !== void 0 ? { socksPort: managedProxyOptions.managedAnonSocksPort } : {}
9346
- });
9347
- try {
9348
- return {
9349
- createWebSocket: createSocks5WebSocketFactory(proxy.socksProxy),
9350
- httpClient: createSocks5Fetch(proxy.socksProxy),
9351
- stopManagedProxy: proxy.stop
9352
- };
9353
- } catch (err) {
9354
- await proxy.stop();
9355
- throw err;
9356
- }
9357
- }
9358
- if (!transport || transport.type === "direct") {
9359
- return {};
9360
- }
9361
- if (transport.type === "socks5") {
9362
- const {
9363
- createSocks5WebSocketFactory,
9364
- createSocks5Fetch,
9365
- probeSocks5Proxy
9366
- } = await import("./socks5-WTJBYGME-6COK4LXW.js");
9367
- await probeSocks5Proxy(transport.socksProxy);
9368
- return {
9369
- createWebSocket: createSocks5WebSocketFactory(transport.socksProxy),
9370
- httpClient: createSocks5Fetch(transport.socksProxy)
9371
- };
9372
- }
9373
- if (transport.type === "gateway") {
9374
- const { rewriteUrlsForGateway } = await import("./gateway-QOK47RKS-SEGTXBR3.js");
9375
- const rewritten = rewriteUrlsForGateway(
9376
- transport.gatewayUrl,
9377
- originalBtpUrl,
9378
- originalConnectorUrl
9379
- );
9380
- return {
9381
- btpUrl: rewritten.btpUrl,
9382
- connectorUrl: rewritten.connectorUrl
9383
- };
9384
- }
9385
- throw new Error(
9386
- `Unknown transport type: "${transport.type}"`
9387
- );
9388
- }
9389
9237
  async function initializeHttpMode(config) {
9390
- const transport = await resolveTransport(
9391
- config.transport,
9392
- config.btpUrl,
9393
- config.connectorUrl,
9394
- {
9395
- ...config.managedAnonProxy !== void 0 ? { managedAnonProxy: config.managedAnonProxy } : {},
9396
- ...config.managedAnonSocksPort !== void 0 ? { managedAnonSocksPort: config.managedAnonSocksPort } : {}
9397
- }
9398
- );
9399
- const effectiveBtpUrl = transport.btpUrl ?? config.btpUrl;
9400
- const effectiveConnectorUrl = transport.connectorUrl ?? config.connectorUrl;
9238
+ const effectiveBtpUrl = config.btpUrl;
9239
+ const effectiveConnectorUrl = config.connectorUrl;
9401
9240
  const settlementInfo = buildSettlementInfo(config);
9241
+ const discoveredPeer = readDiscoveredIlpPeer({
9242
+ btpEndpoint: effectiveBtpUrl,
9243
+ httpEndpoint: config.connectorHttpEndpoint,
9244
+ supportsUpgrade: config.connectorSupportsUpgrade
9245
+ });
9246
+ const transportChoice = discoveredPeer.httpEndpoint || discoveredPeer.btpEndpoint ? selectIlpTransport(discoveredPeer, { needsDuplex: false }) : null;
9402
9247
  let btpClient = null;
9403
9248
  if (effectiveBtpUrl) {
9404
9249
  btpClient = new BtpRuntimeClient({
9405
9250
  btpUrl: effectiveBtpUrl,
9406
9251
  peerId: config.btpPeerId ?? `client`,
9407
- authToken: config.btpAuthToken ?? "",
9408
- createWebSocket: transport.createWebSocket
9252
+ authToken: config.btpAuthToken ?? ""
9409
9253
  });
9410
9254
  await btpClient.connect();
9411
9255
  }
9412
- const runtimeClient = btpClient ?? new HttpRuntimeClient({
9256
+ let httpIlpClient = null;
9257
+ if (transportChoice && (transportChoice.kind === "http" || transportChoice.kind === "http-upgradable")) {
9258
+ httpIlpClient = new HttpIlpClient({
9259
+ httpEndpoint: transportChoice.httpEndpoint,
9260
+ ...config.btpPeerId !== void 0 ? { peerId: config.btpPeerId } : {},
9261
+ ...config.btpAuthToken !== void 0 ? { authToken: config.btpAuthToken } : {},
9262
+ timeout: config.queryTimeout,
9263
+ maxRetries: config.maxRetries,
9264
+ retryDelay: config.retryDelay
9265
+ });
9266
+ }
9267
+ const runtimeClient = httpIlpClient ?? btpClient ?? new HttpRuntimeClient({
9413
9268
  connectorUrl: effectiveConnectorUrl,
9414
9269
  timeout: config.queryTimeout,
9415
9270
  maxRetries: config.maxRetries,
9416
- retryDelay: config.retryDelay,
9417
- httpClient: transport.httpClient
9271
+ retryDelay: config.retryDelay
9418
9272
  });
9419
9273
  let onChainChannelClient = null;
9420
9274
  if (config.chainRpcUrls) {
@@ -9459,10 +9313,7 @@ async function initializeHttpMode(config) {
9459
9313
  runtimeClient,
9460
9314
  adminClient: null,
9461
9315
  btpClient,
9462
- onChainChannelClient,
9463
- // Teardown handle for a managed `anon` proxy this init STARTED (undefined
9464
- // for explicit-proxy/direct/gateway). ToonClient.stop() invokes it.
9465
- stopManagedProxy: transport.stopManagedProxy
9316
+ onChainChannelClient
9466
9317
  };
9467
9318
  }
9468
9319
  var SolanaSigner = class {
@@ -9666,7 +9517,7 @@ var DEFAULT_MINA_TOKEN_ID = "MINA";
9666
9517
  var MINA_CLAIM_NETWORK = "devnet";
9667
9518
  function deriveMinaSalt(zkAppAddress, nonce) {
9668
9519
  const digestHex = bytesToHex(
9669
- sha256(new TextEncoder().encode(`mina-pc-salt:${zkAppAddress}:${nonce}`))
9520
+ sha2562(new TextEncoder().encode(`mina-pc-salt:${zkAppAddress}:${nonce}`))
9670
9521
  );
9671
9522
  const salt = BigInt("0x" + digestHex.slice(0, 60));
9672
9523
  return salt === 0n ? 1n : salt;
@@ -10134,6 +9985,319 @@ var JsonFileChannelStore = class {
10134
9985
  writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf-8");
10135
9986
  }
10136
9987
  };
9988
+ var ARWEAVE_TX_ID_REGEX = /^[A-Za-z0-9_-]{43}$/;
9989
+ async function requestBlobStorage(client, secretKey, params) {
9990
+ const bid = params.bid ?? (params.ilpAmount !== void 0 ? String(params.ilpAmount) : void 0);
9991
+ if (bid === void 0 || bid === "") {
9992
+ return {
9993
+ success: false,
9994
+ error: "requestBlobStorage requires a bid (or ilpAmount to derive it)"
9995
+ };
9996
+ }
9997
+ const blobBuffer = Buffer.from(
9998
+ params.blobData.buffer,
9999
+ params.blobData.byteOffset,
10000
+ params.blobData.byteLength
10001
+ );
10002
+ let event;
10003
+ try {
10004
+ event = buildBlobStorageRequest(
10005
+ {
10006
+ blobData: blobBuffer,
10007
+ contentType: params.contentType,
10008
+ bid
10009
+ },
10010
+ secretKey
10011
+ );
10012
+ } catch (error) {
10013
+ return {
10014
+ success: false,
10015
+ error: error instanceof Error ? error.message : String(error)
10016
+ };
10017
+ }
10018
+ const result = await client.publishEvent(event, {
10019
+ destination: params.destination,
10020
+ claim: params.claim,
10021
+ ilpAmount: params.ilpAmount
10022
+ });
10023
+ if (!result.success) {
10024
+ return {
10025
+ success: false,
10026
+ eventId: result.eventId ?? event.id,
10027
+ error: result.error ?? "Blob storage request rejected"
10028
+ };
10029
+ }
10030
+ if (!result.data) {
10031
+ return {
10032
+ success: false,
10033
+ eventId: event.id,
10034
+ error: "FULFILL contained no data; expected base64-encoded Arweave tx ID"
10035
+ };
10036
+ }
10037
+ const txId = decodeUtf8(fromBase64(result.data));
10038
+ if (!ARWEAVE_TX_ID_REGEX.test(txId)) {
10039
+ return {
10040
+ success: false,
10041
+ eventId: event.id,
10042
+ error: `Decoded FULFILL data is not a valid Arweave tx ID: "${txId}"`
10043
+ };
10044
+ }
10045
+ return {
10046
+ success: true,
10047
+ txId,
10048
+ eventId: event.id
10049
+ };
10050
+ }
10051
+ var Http402Client = class {
10052
+ fetchImpl;
10053
+ resolveClaim;
10054
+ createIlpClient;
10055
+ needsDuplex;
10056
+ constructor(config = {}) {
10057
+ this.fetchImpl = config.fetch ?? fetch;
10058
+ this.resolveClaim = config.resolveClaim;
10059
+ this.createIlpClient = config.createIlpClient ?? ((httpEndpoint) => new HttpIlpClient({ httpEndpoint }));
10060
+ this.needsDuplex = config.needsDuplex ?? false;
10061
+ }
10062
+ /**
10063
+ * `fetch()`-like entry point. Issues the request; on `402` parses the x402
10064
+ * challenge and — when a usable `toon-channel` offer is present and a claim
10065
+ * resolver is configured — pays over TOON and returns the reconstructed
10066
+ * `Response`. Otherwise returns the original 402 unchanged (AC5).
10067
+ */
10068
+ async fetch(url, opts = {}) {
10069
+ const method = (opts.method ?? "GET").toUpperCase();
10070
+ const probe = await this.fetchImpl(url, {
10071
+ method,
10072
+ ...opts.headers ? { headers: opts.headers } : {},
10073
+ ...opts.body !== void 0 ? { body: opts.body } : {},
10074
+ ...opts.timeout !== void 0 ? { signal: AbortSignal.timeout(opts.timeout) } : {}
10075
+ });
10076
+ if (probe.status !== 402) return probe;
10077
+ const challenge = await parseX402Challenge(probe.clone());
10078
+ const accept = challenge.toonChannel;
10079
+ if (!accept || !this.resolveClaim) return probe;
10080
+ return this.payOverToon(url, method, opts, accept, this.resolveClaim);
10081
+ }
10082
+ /**
10083
+ * Open/reuse a channel (via the injected claim resolver), serialize the HTTP
10084
+ * request into the ILP packet `data`, send it to `POST /ilp` with the claim,
10085
+ * and reconstruct the origin `Response` from the FULFILL `data`.
10086
+ */
10087
+ async payOverToon(url, method, opts, accept, resolveClaim) {
10088
+ const destination = opts.destination ?? accept.destination;
10089
+ const claim = await resolveClaim(destination, accept.amount);
10090
+ const requestBytes = serializeHttpRequest({
10091
+ method,
10092
+ url,
10093
+ headers: opts.headers,
10094
+ body: opts.body
10095
+ });
10096
+ const peer = {
10097
+ httpEndpoint: accept.httpEndpoint,
10098
+ supportsUpgrade: accept.supportsUpgrade
10099
+ };
10100
+ const choice = selectIlpTransport(peer, {
10101
+ needsDuplex: this.needsDuplex
10102
+ });
10103
+ const ilpClient = this.createIlpClient(accept.httpEndpoint);
10104
+ const result = await this.sendOverChoice(
10105
+ ilpClient,
10106
+ choice,
10107
+ {
10108
+ destination,
10109
+ amount: String(accept.amount),
10110
+ data: toBase64(requestBytes),
10111
+ ...opts.timeout !== void 0 ? { timeout: opts.timeout } : {}
10112
+ },
10113
+ claim
10114
+ );
10115
+ if (!result.accepted) {
10116
+ throw new ConnectorError(
10117
+ `h402 payment rejected by connector: ${result.code ?? "F00"} ${result.message ?? ""}`.trim()
10118
+ );
10119
+ }
10120
+ if (!result.data) {
10121
+ throw new ConnectorError(
10122
+ "h402 FULFILL carried no data (expected an HTTP response payload)"
10123
+ );
10124
+ }
10125
+ return parseHttpResponse(fromBase64(result.data));
10126
+ }
10127
+ /**
10128
+ * Send the serialized HTTP-in-ILP PREPARE over the selected transport.
10129
+ *
10130
+ * - `http` / `http-upgradable`: stateless one-shot `POST /ilp` with the claim.
10131
+ * - `http-upgradable` additionally exercises {@link HttpIlpClient.upgradeToBtp}
10132
+ * for the duplex/streaming path (AC4). v1 still drives the actual write over
10133
+ * the one-shot HTTP method even after upgrading — full duplex body streaming
10134
+ * is a documented follow-up — but the upgrade call path is wired here.
10135
+ * - `btp`: not reachable from h402 (the x402 offer only carries an
10136
+ * `httpEndpoint`); guarded for completeness.
10137
+ */
10138
+ async sendOverChoice(ilpClient, choice, params, claim) {
10139
+ if (choice.kind === "http-upgradable") {
10140
+ const btp = await ilpClient.upgradeToBtp();
10141
+ try {
10142
+ return await btp.sendIlpPacketWithClaim(
10143
+ params,
10144
+ claim
10145
+ );
10146
+ } finally {
10147
+ await btp.disconnect().catch(() => {
10148
+ });
10149
+ }
10150
+ }
10151
+ if (choice.kind === "btp") {
10152
+ throw new ToonClientError(
10153
+ "h402 offer resolved to a BTP-only transport; the x402 toon-channel entry must advertise an httpEndpoint",
10154
+ "INVALID_STATE"
10155
+ );
10156
+ }
10157
+ return ilpClient.sendIlpPacketWithClaim(params, claim);
10158
+ }
10159
+ };
10160
+ function readString(obj, keys) {
10161
+ for (const k of keys) {
10162
+ const v = obj[k];
10163
+ if (typeof v === "string" && v.trim().length > 0) return v.trim();
10164
+ }
10165
+ return void 0;
10166
+ }
10167
+ function readAmount(obj, keys) {
10168
+ for (const k of keys) {
10169
+ const v = obj[k];
10170
+ if (typeof v === "bigint") return v;
10171
+ if (typeof v === "number" && Number.isFinite(v)) return BigInt(Math.trunc(v));
10172
+ if (typeof v === "string" && /^\d+$/.test(v.trim())) return BigInt(v.trim());
10173
+ }
10174
+ return void 0;
10175
+ }
10176
+ async function parseX402Challenge(response) {
10177
+ let body;
10178
+ try {
10179
+ body = await response.json();
10180
+ } catch {
10181
+ return {};
10182
+ }
10183
+ return parseX402Body(body);
10184
+ }
10185
+ function parseX402Body(body) {
10186
+ if (typeof body !== "object" || body === null) return {};
10187
+ const b = body;
10188
+ const version = typeof b["x402Version"] === "number" ? b["x402Version"] : void 0;
10189
+ const accepts = Array.isArray(b["accepts"]) ? b["accepts"] : [];
10190
+ for (const raw of accepts) {
10191
+ if (typeof raw !== "object" || raw === null) continue;
10192
+ const entry = raw;
10193
+ const scheme = readString(entry, ["scheme"]);
10194
+ if (scheme !== "toon-channel") continue;
10195
+ const destination = readString(entry, [
10196
+ "destination",
10197
+ "ilpAddress",
10198
+ "payTo"
10199
+ ]);
10200
+ const httpEndpoint = readString(entry, [
10201
+ "httpEndpoint",
10202
+ "ilpEndpoint",
10203
+ "endpoint"
10204
+ ]);
10205
+ const amount = readAmount(entry, ["amount", "price", "maxAmountRequired"]);
10206
+ if (!destination || !httpEndpoint || amount === void 0) continue;
10207
+ const network = readString(entry, ["network", "chain"]);
10208
+ const supportsUpgrade = entry["supportsUpgrade"] === true || entry["upgradable"] === true;
10209
+ return {
10210
+ ...version !== void 0 ? { x402Version: version } : {},
10211
+ toonChannel: {
10212
+ scheme: "toon-channel",
10213
+ ...network !== void 0 ? { network } : {},
10214
+ destination,
10215
+ amount,
10216
+ httpEndpoint,
10217
+ supportsUpgrade
10218
+ }
10219
+ };
10220
+ }
10221
+ return version !== void 0 ? { x402Version: version } : {};
10222
+ }
10223
+ var CRLF = "\r\n";
10224
+ function concatHeadAndBody(head, body) {
10225
+ const headBytes = encodeUtf8(head);
10226
+ const out = new Uint8Array(headBytes.length + body.length);
10227
+ out.set(headBytes, 0);
10228
+ out.set(body, headBytes.length);
10229
+ return out;
10230
+ }
10231
+ function bodyToBytes(body) {
10232
+ if (body === void 0) return new Uint8Array(0);
10233
+ return typeof body === "string" ? encodeUtf8(body) : body;
10234
+ }
10235
+ function serializeHttpRequest(req) {
10236
+ const u = new URL(req.url);
10237
+ const target = `${u.pathname}${u.search}` || "/";
10238
+ const bodyBytes = bodyToBytes(req.body);
10239
+ const headers = /* @__PURE__ */ new Map();
10240
+ const put = (name, value) => headers.set(name.toLowerCase(), `${name}: ${value}`);
10241
+ const has = (name) => headers.has(name.toLowerCase());
10242
+ for (const [name, value] of Object.entries(req.headers ?? {})) {
10243
+ put(name, value);
10244
+ }
10245
+ if (!has("host")) put("Host", u.host);
10246
+ if (bodyBytes.length > 0 && !has("content-length")) {
10247
+ put("Content-Length", String(bodyBytes.length));
10248
+ }
10249
+ const lines = [
10250
+ `${req.method.toUpperCase()} ${target} HTTP/1.1`,
10251
+ ...headers.values()
10252
+ ];
10253
+ const head = lines.join(CRLF) + CRLF + CRLF;
10254
+ return concatHeadAndBody(head, bodyBytes);
10255
+ }
10256
+ function findHeaderEnd(bytes) {
10257
+ for (let i = 0; i + 3 < bytes.length; i++) {
10258
+ if (bytes[i] === 13 && bytes[i + 1] === 10 && bytes[i + 2] === 13 && bytes[i + 3] === 10) {
10259
+ return i + 4;
10260
+ }
10261
+ }
10262
+ return -1;
10263
+ }
10264
+ function parseHttpResponse(bytes) {
10265
+ const headerEnd = findHeaderEnd(bytes);
10266
+ const headBytes = headerEnd === -1 ? bytes : bytes.subarray(0, headerEnd - 2);
10267
+ const body = headerEnd === -1 ? new Uint8Array(0) : bytes.subarray(headerEnd);
10268
+ const headText = decodeUtf8(headBytes);
10269
+ const lines = headText.split(CRLF).filter((l) => l.length > 0);
10270
+ const statusLine = lines.shift();
10271
+ if (!statusLine) {
10272
+ throw new ConnectorError(
10273
+ "h402 response payload had no HTTP status line"
10274
+ );
10275
+ }
10276
+ const match = /^HTTP\/\d\.\d\s+(\d{3})(?:\s+(.*))?$/.exec(statusLine.trim());
10277
+ if (!match) {
10278
+ throw new ConnectorError(
10279
+ `h402 response payload had a malformed status line: "${statusLine}"`
10280
+ );
10281
+ }
10282
+ const status = parseInt(match[1], 10);
10283
+ const statusText = match[2] ?? "";
10284
+ const headers = new Headers();
10285
+ for (const line of lines) {
10286
+ const idx = line.indexOf(":");
10287
+ if (idx === -1) continue;
10288
+ const name = line.slice(0, idx).trim();
10289
+ const value = line.slice(idx + 1).trim();
10290
+ if (name.length === 0) continue;
10291
+ headers.append(name, value);
10292
+ }
10293
+ const nullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
10294
+ const init = { status, headers };
10295
+ if (statusText) init.statusText = statusText;
10296
+ return new Response(
10297
+ nullBodyStatus || body.length === 0 ? null : body.slice(),
10298
+ init
10299
+ );
10300
+ }
10137
10301
  var ToonClient = class {
10138
10302
  config;
10139
10303
  state = null;
@@ -10184,6 +10348,28 @@ var ToonClient = class {
10184
10348
  getPublicKey() {
10185
10349
  return getPublicKey22(this.config.secretKey);
10186
10350
  }
10351
+ /**
10352
+ * Sign an unsigned Nostr event template with the client's Nostr secret key,
10353
+ * returning a fully-signed event (id + pubkey + sig).
10354
+ *
10355
+ * This is the key primitive behind the daemon's sign-and-publish path: a UI
10356
+ * or agent supplies only `{ kind, content, tags, created_at }` and never holds
10357
+ * the private key — signing happens here, inside the key owner.
10358
+ */
10359
+ signEvent(template) {
10360
+ return finalizeEvent11(template, this.config.secretKey);
10361
+ }
10362
+ /**
10363
+ * Upload bytes to Arweave via the kind:5094 blob-storage DVM (single-packet),
10364
+ * signing the request with this client's Nostr key and paying through its
10365
+ * existing channel. Returns the Arweave tx id on success.
10366
+ *
10367
+ * Backs the daemon's `upload-media` path: the key and claim/channel plumbing
10368
+ * stay inside the client; callers pass only the bytes.
10369
+ */
10370
+ async uploadBlob(params) {
10371
+ return requestBlobStorage(this, this.config.secretKey, params);
10372
+ }
10187
10373
  /**
10188
10374
  * Per-chain settlement readiness for the configured `network` tier, mirroring
10189
10375
  * the townhouse node's status. Returns `undefined` when no named `network` is
@@ -10266,13 +10452,7 @@ var ToonClient = class {
10266
10452
  }
10267
10453
  }
10268
10454
  const initialization = await initializeHttpMode(this.config);
10269
- const {
10270
- bootstrapService,
10271
- discoveryTracker,
10272
- runtimeClient,
10273
- btpClient,
10274
- stopManagedProxy
10275
- } = initialization;
10455
+ const { bootstrapService, discoveryTracker, runtimeClient, btpClient } = initialization;
10276
10456
  if (this.channelManager) {
10277
10457
  const cm = this.channelManager;
10278
10458
  const nostrPubkey = this.getPublicKey();
@@ -10360,8 +10540,7 @@ var ToonClient = class {
10360
10540
  discoveryTracker,
10361
10541
  runtimeClient,
10362
10542
  peersDiscovered: bootstrapResults.length,
10363
- btpClient: btpClient ?? void 0,
10364
- ...stopManagedProxy ? { stopManagedProxy } : {}
10543
+ btpClient: btpClient ?? void 0
10365
10544
  };
10366
10545
  return {
10367
10546
  peersDiscovered: bootstrapResults.length,
@@ -10397,13 +10576,9 @@ var ToonClient = class {
10397
10576
  const toonData = this.config.toonEncoder(event);
10398
10577
  const basePricePerByte = 10n;
10399
10578
  const amount = options?.ilpAmount !== void 0 ? String(options.ilpAmount) : String(BigInt(toonData.length) * basePricePerByte);
10579
+ const writeData = buildStoreWriteEnvelope(event);
10400
10580
  const destination = options?.destination ?? this.config.destinationAddress;
10401
- if (!this.state.btpClient) {
10402
- throw new ToonClientError(
10403
- "BTP client required for publishing. Configure btpUrl.",
10404
- "NO_BTP_CLIENT"
10405
- );
10406
- }
10581
+ const transport = this.getClaimTransport();
10407
10582
  let claimMessage;
10408
10583
  if (options?.claim) {
10409
10584
  claimMessage = this.buildClaimMessageForProof(options.claim);
@@ -10432,13 +10607,11 @@ var ToonClient = class {
10432
10607
  "MISSING_CLAIM"
10433
10608
  );
10434
10609
  }
10435
- const response = await this.state.btpClient.sendIlpPacketWithClaim(
10610
+ const response = await transport.sendIlpPacketWithClaim(
10436
10611
  {
10437
10612
  destination,
10438
10613
  amount,
10439
- data: toBase64(
10440
- toonData instanceof Uint8Array ? toonData : new Uint8Array(toonData)
10441
- )
10614
+ data: toBase64(writeData)
10442
10615
  },
10443
10616
  claimMessage
10444
10617
  );
@@ -10466,6 +10639,46 @@ var ToonClient = class {
10466
10639
  );
10467
10640
  }
10468
10641
  }
10642
+ /**
10643
+ * Payment-aware HTTP fetch over TOON (issue #50). A `fetch()`-like method that
10644
+ * makes paying for an HTTP resource transparent:
10645
+ *
10646
+ * 1. Issues the HTTP request to `url`.
10647
+ * 2. On `402`, parses the x402 `accepts` array and selects the
10648
+ * `toon-channel` entry (see {@link Http402Client} for the wire shape).
10649
+ * 3. Opens/reuses a payment channel for the entry's ILP destination (via
10650
+ * ChannelManager), signs a balance proof for the demanded price, and
10651
+ * re-sends the SAME HTTP request as a transparent HTTP-in-ILP packet to
10652
+ * the connector's `POST /ilp` (via {@link HttpIlpClient}), with the claim
10653
+ * in the `ILP-Payment-Channel-Claim` header.
10654
+ * 4. Reconstructs and returns a standard Web `Response` from the FULFILL
10655
+ * `data`. The caller never sees ILP.
10656
+ *
10657
+ * If the origin offers no `toon-channel` entry, the original `402` Response is
10658
+ * returned unchanged (the caller sees the vanilla x402 challenge).
10659
+ *
10660
+ * The channel/claim plumbing is wired to the live ChannelManager + per-chain
10661
+ * signer via `resolveClaimForDestination` — identical to `publishEvent`. The
10662
+ * `amount` paid comes from the selected x402 entry (the resource's price).
10663
+ *
10664
+ * @throws {ToonClientError} If the client is not started.
10665
+ * @throws {ConnectorError} If the connector rejects the payment or returns no
10666
+ * HTTP payload.
10667
+ */
10668
+ async h402Fetch(url, opts) {
10669
+ if (!this.state) {
10670
+ throw new ToonClientError(
10671
+ "Client not started. Call start() first.",
10672
+ "INVALID_STATE"
10673
+ );
10674
+ }
10675
+ const client = new Http402Client({
10676
+ ...this.channelManager ? {
10677
+ resolveClaim: (destination, amount) => this.resolveClaimForDestination(destination, amount)
10678
+ } : {}
10679
+ });
10680
+ return client.fetch(url, opts);
10681
+ }
10469
10682
  /**
10470
10683
  * Sends a raw swap ILP packet (Story 12.5) to a Mill peer with an attached
10471
10684
  * balance-proof claim. This is a lower-level surface than `publishEvent`:
@@ -10478,7 +10691,7 @@ var ToonClient = class {
10478
10691
  * matching `destination`,
10479
10692
  * (c) neither -> throw MISSING_CLAIM.
10480
10693
  *
10481
- * @throws {ToonClientError} INVALID_STATE / NO_BTP_CLIENT / MISSING_CLAIM
10694
+ * @throws {ToonClientError} INVALID_STATE / NO_ILP_TRANSPORT / MISSING_CLAIM
10482
10695
  */
10483
10696
  async sendSwapPacket(params) {
10484
10697
  if (!this.state) {
@@ -10487,18 +10700,13 @@ var ToonClient = class {
10487
10700
  "INVALID_STATE"
10488
10701
  );
10489
10702
  }
10490
- if (!this.state.btpClient) {
10491
- throw new ToonClientError(
10492
- "BTP client required for sending swap packets. Configure btpUrl.",
10493
- "NO_BTP_CLIENT"
10494
- );
10495
- }
10703
+ const transport = this.getClaimTransport();
10496
10704
  const claimMessage = await this.resolveClaimForDestination(
10497
10705
  params.destination,
10498
10706
  params.amount,
10499
10707
  params.claim
10500
10708
  );
10501
- return this.state.btpClient.sendIlpPacketWithClaim(
10709
+ return transport.sendIlpPacketWithClaim(
10502
10710
  {
10503
10711
  destination: params.destination,
10504
10712
  amount: String(params.amount),
@@ -10538,6 +10746,51 @@ var ToonClient = class {
10538
10746
  }
10539
10747
  return EvmSigner.buildClaimMessage(claim, this.getPublicKey());
10540
10748
  }
10749
+ /**
10750
+ * Resolve the ILP transport for a paid (claim-bearing) write.
10751
+ *
10752
+ * The connector is a payment-proxy: paid writes carry an ILP PREPARE plus the
10753
+ * signed payment-channel claim. Either transport speaks the SAME claim
10754
+ * contract — the BTP `payment-channel-claim` protocolData entry and the
10755
+ * ILP-over-HTTP `ILP-Payment-Channel-Claim` header serialize the same claim
10756
+ * JSON — so we route through whichever transport is ACTIVE rather than
10757
+ * hard-requiring BTP.
10758
+ *
10759
+ * Selection (mirrors `modes/http.ts` runtime-client precedence):
10760
+ * 1. `runtimeClient` when it implements `sendIlpPacketWithClaim` — this is
10761
+ * the HttpIlpClient (proxy `POST /ilp`) when a `proxyUrl`/
10762
+ * `connectorHttpEndpoint` is configured, else the BtpRuntimeClient.
10763
+ * 2. `btpClient` as an explicit fallback (always present when `btpUrl` is set).
10764
+ *
10765
+ * The level-3 `HttpRuntimeClient` (connector-admin HTTP, no `btpUrl` AND no
10766
+ * proxy) does NOT implement `sendIlpPacketWithClaim`; in that case there is no
10767
+ * paid-write transport and we throw a clear, actionable error.
10768
+ *
10769
+ * @throws {ToonClientError} NO_ILP_TRANSPORT when no active transport can send
10770
+ * a packet+claim.
10771
+ */
10772
+ getClaimTransport() {
10773
+ const state = this.state;
10774
+ if (!state) {
10775
+ throw new ToonClientError(
10776
+ "Client not started. Call start() first.",
10777
+ "INVALID_STATE"
10778
+ );
10779
+ }
10780
+ const candidates = [
10781
+ state.runtimeClient,
10782
+ state.btpClient
10783
+ ];
10784
+ for (const candidate of candidates) {
10785
+ if (candidate && typeof candidate.sendIlpPacketWithClaim === "function") {
10786
+ return candidate;
10787
+ }
10788
+ }
10789
+ throw new ToonClientError(
10790
+ "No ILP transport for paid writes. Configure `proxyUrl`/`connectorHttpEndpoint` (route through the connector proxy over ILP-over-HTTP) or `btpUrl` (BTP socket).",
10791
+ "NO_ILP_TRANSPORT"
10792
+ );
10793
+ }
10541
10794
  /**
10542
10795
  * Shared claim-resolution logic used by `publishEvent` and `sendSwapPacket`.
10543
10796
  * TODO(12.5 followup): also factor `publishEvent`'s inline claim resolution
@@ -10726,14 +10979,9 @@ var ToonClient = class {
10726
10979
  "MISSING_CLAIM"
10727
10980
  );
10728
10981
  }
10729
- if (!this.state.btpClient) {
10730
- throw new ToonClientError(
10731
- "BTP client required for sending payments. Configure btpUrl.",
10732
- "NO_BTP_CLIENT"
10733
- );
10734
- }
10982
+ const transport = this.getClaimTransport();
10735
10983
  const claimMessage = this.buildClaimMessageForProof(params.claim);
10736
- return this.state.btpClient.sendIlpPacketWithClaim(
10984
+ return transport.sendIlpPacketWithClaim(
10737
10985
  ilpParams,
10738
10986
  claimMessage
10739
10987
  );
@@ -10751,14 +10999,10 @@ var ToonClient = class {
10751
10999
  if (!this.state) {
10752
11000
  throw new ToonClientError("Client not started", "INVALID_STATE");
10753
11001
  }
10754
- const stopManagedProxy = this.state.stopManagedProxy;
10755
11002
  try {
10756
11003
  if (this.state.btpClient) {
10757
11004
  await this.state.btpClient.disconnect();
10758
11005
  }
10759
- if (stopManagedProxy) {
10760
- await stopManagedProxy();
10761
- }
10762
11006
  this.state = null;
10763
11007
  } catch (error) {
10764
11008
  throw new ToonClientError(
@@ -10961,71 +11205,40 @@ function resolveMnemonic(file) {
10961
11205
  }
10962
11206
  function resolveConfig(file) {
10963
11207
  const mnemonic = resolveMnemonic(file);
11208
+ const proxyUrl = process.env["TOON_CLIENT_PROXY_URL"] ?? file.proxyUrl;
11209
+ const faucetUrl = process.env["TOON_CLIENT_FAUCET_URL"] ?? file.faucetUrl;
10964
11210
  const btpUrl = process.env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl;
10965
- if (!btpUrl) {
10966
- throw new Error(
10967
- "No btpUrl configured. Set TOON_CLIENT_BTP_URL or add `btpUrl` to the config file."
10968
- );
10969
- }
11211
+ const hasUplink = Boolean(btpUrl || proxyUrl);
10970
11212
  const relayUrl = process.env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl ?? "ws://localhost:7100";
10971
- const explicitSocks = process.env["TOON_CLIENT_SOCKS"] ?? file.socksProxy;
10972
11213
  const httpPort = Number(
10973
11214
  process.env["TOON_CLIENT_HTTP_PORT"] ?? file.httpPort ?? 8787
10974
11215
  );
10975
- const destination = file.destination ?? "g.townhouse.town";
11216
+ const destination = process.env["TOON_CLIENT_DESTINATION"] ?? file.destination ?? "g.townhouse.town";
10976
11217
  const feePerEvent = BigInt(file.feePerEvent ?? "1");
10977
11218
  const network = process.env["TOON_CLIENT_NETWORK"] ?? file.network;
10978
11219
  const chain2 = process.env["TOON_CLIENT_CHAIN"] ?? file.chain ?? "evm";
10979
- const apex = file.apexChains?.[chain2] ?? file.apex;
10980
- const managedPort = Number(file.managedAnonSocksPort ?? 9050);
10981
- const allowManaged = file.managedAnonProxy ?? true;
10982
- const btpIsAnyone = isAnyoneHost2(btpUrl);
10983
- const relayIsAnyone = isAnyoneHost2(relayUrl);
10984
- let transport;
10985
- let managedAnonProxy;
10986
- let managedAnonSocksPort;
10987
- let readsSocksProxy;
10988
- let manageReadProxy = false;
10989
- if (explicitSocks) {
10990
- transport = { type: "socks5", socksProxy: explicitSocks };
10991
- managedAnonProxy = false;
10992
- readsSocksProxy = explicitSocks;
10993
- } else if (allowManaged && btpIsAnyone) {
10994
- transport = { type: "direct" };
10995
- managedAnonProxy = true;
10996
- managedAnonSocksPort = managedPort;
10997
- readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
10998
- } else if (allowManaged && relayIsAnyone) {
10999
- transport = { type: "direct" };
11000
- managedAnonProxy = false;
11001
- manageReadProxy = true;
11002
- managedAnonSocksPort = managedPort;
11003
- readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
11004
- } else {
11005
- transport = { type: "direct" };
11006
- managedAnonProxy = false;
11007
- }
11220
+ const apex = file.apexChains?.[chain2] ?? file.apex ?? buildProxyApexNegotiation(file, chain2, destination);
11008
11221
  const channelStorePath = file.channelStorePath ?? join2(configDir(), "channels.json");
11009
11222
  const apexChannelStorePath = join2(configDir(), "apex-channels.json");
11010
11223
  const toonClientConfig = {
11011
- // Required by validateConfig but unused at runtime (BTP transport is used).
11012
- connectorUrl: "http://127.0.0.1:1",
11224
+ // validateConfig requires connectorUrl OR proxyUrl. When only BTP is set
11225
+ // we pass a dummy connectorUrl (unused at runtime — BTP transport is used);
11226
+ // when a proxy is configured, `proxyUrl` satisfies the requirement and the
11227
+ // client derives the `POST /ilp` endpoint + routes writes over HTTP.
11228
+ ...proxyUrl ? { proxyUrl } : { connectorUrl: "http://127.0.0.1:1" },
11229
+ ...faucetUrl ? { faucetUrl } : {},
11013
11230
  mnemonic,
11014
11231
  mnemonicAccountIndex: file.mnemonicAccountIndex ?? 0,
11015
11232
  ilpInfo: {
11016
11233
  pubkey: "00".repeat(32),
11017
11234
  ilpAddress: "g.toon.client",
11018
- btpEndpoint: btpUrl,
11235
+ btpEndpoint: btpUrl ?? "",
11019
11236
  assetCode: "USD",
11020
11237
  assetScale: 6
11021
11238
  },
11022
11239
  toonEncoder: encodeEventToToon,
11023
11240
  toonDecoder: decodeEventFromToon,
11024
- btpUrl,
11025
- btpAuthToken: "",
11026
- transport,
11027
- managedAnonProxy,
11028
- ...managedAnonSocksPort !== void 0 ? { managedAnonSocksPort } : {},
11241
+ ...btpUrl ? { btpUrl, btpAuthToken: "" } : {},
11029
11242
  destinationAddress: destination,
11030
11243
  relayUrl: "",
11031
11244
  // reads use our own RelaySubscription, not bootstrap discovery
@@ -11043,12 +11256,12 @@ function resolveConfig(file) {
11043
11256
  return {
11044
11257
  httpPort,
11045
11258
  relayUrl,
11046
- ...readsSocksProxy !== void 0 ? { socksProxy: readsSocksProxy } : {},
11047
- manageReadProxy,
11048
- ...manageReadProxy ? { readProxySocksPort: managedPort } : {},
11259
+ hasUplink,
11260
+ ...proxyUrl ? { proxyUrl } : {},
11261
+ ...faucetUrl ? { faucetUrl } : {},
11049
11262
  destination,
11050
11263
  feePerEvent,
11051
- apex,
11264
+ ...apex ? { apex } : {},
11052
11265
  ...file.apexChildPeers ? { apexChildPeers: file.apexChildPeers } : {},
11053
11266
  chain: chain2,
11054
11267
  apexChannelStorePath,
@@ -11056,12 +11269,38 @@ function resolveConfig(file) {
11056
11269
  network
11057
11270
  };
11058
11271
  }
11059
- function isAnyoneHost2(url) {
11060
- try {
11061
- return new URL(url).hostname.endsWith(".anyone");
11062
- } catch {
11063
- return false;
11064
- }
11272
+ function defaultChainKey(chain2, chainId) {
11273
+ switch (chain2) {
11274
+ case "evm":
11275
+ return `evm:devnet:${chainId}`;
11276
+ case "solana":
11277
+ return "solana:devnet";
11278
+ case "mina":
11279
+ return "mina:devnet";
11280
+ }
11281
+ }
11282
+ function buildProxyApexNegotiation(file, chain2, destination) {
11283
+ const proxyUrl = process.env["TOON_CLIENT_PROXY_URL"] ?? file.proxyUrl;
11284
+ if (!proxyUrl) return void 0;
11285
+ const settlementAddresses = file.settlementAddresses ?? {};
11286
+ const familyKeys = (rec) => Object.keys(rec).filter((k) => k.split(":")[0] === chain2);
11287
+ const chainKey = familyKeys(settlementAddresses)[0] ?? familyKeys(file.tokenNetworks ?? {})[0] ?? familyKeys(file.preferredTokens ?? {})[0];
11288
+ if (!chainKey) return void 0;
11289
+ const settlementAddress = settlementAddresses[chainKey];
11290
+ if (!settlementAddress) return void 0;
11291
+ const parts = chainKey.split(":");
11292
+ const chainId = chain2 === "evm" && parts.length >= 3 ? Number(parts[2] ?? 0) : 0;
11293
+ const peerId = destination.split(".").at(-1) ?? destination;
11294
+ return {
11295
+ destination,
11296
+ peerId,
11297
+ chain: chain2,
11298
+ chainKey: chainKey || defaultChainKey(chain2, chainId),
11299
+ chainId,
11300
+ settlementAddress,
11301
+ ...file.preferredTokens?.[chainKey] ? { tokenAddress: file.preferredTokens[chainKey] } : {},
11302
+ ...file.tokenNetworks?.[chainKey] ? { tokenNetwork: file.tokenNetworks[chainKey] } : {}
11303
+ };
11065
11304
  }
11066
11305
 
11067
11306
  // src/control-client.ts
@@ -11073,6 +11312,9 @@ var ControlApiError = class extends Error {
11073
11312
  this.detail = detail;
11074
11313
  this.name = "ControlApiError";
11075
11314
  }
11315
+ status;
11316
+ retryable;
11317
+ detail;
11076
11318
  };
11077
11319
  var DaemonUnreachableError = class extends Error {
11078
11320
  constructor(baseUrl, causedBy) {
@@ -11081,6 +11323,8 @@ var DaemonUnreachableError = class extends Error {
11081
11323
  this.causedBy = causedBy;
11082
11324
  this.name = "DaemonUnreachableError";
11083
11325
  }
11326
+ baseUrl;
11327
+ causedBy;
11084
11328
  };
11085
11329
  var ControlClient = class {
11086
11330
  baseUrl;
@@ -11107,9 +11351,18 @@ var ControlClient = class {
11107
11351
  publish(body) {
11108
11352
  return this.request("POST", "/publish", body);
11109
11353
  }
11354
+ publishUnsigned(body) {
11355
+ return this.request("POST", "/publish-unsigned", body);
11356
+ }
11357
+ uploadMedia(body) {
11358
+ return this.request("POST", "/upload-media", body);
11359
+ }
11110
11360
  subscribe(body) {
11111
11361
  return this.request("POST", "/subscribe", body);
11112
11362
  }
11363
+ query(body) {
11364
+ return this.request("POST", "/query", body);
11365
+ }
11113
11366
  events(query = {}) {
11114
11367
  const qs = new URLSearchParams();
11115
11368
  if (query.subId) qs.set("subId", query.subId);
@@ -11128,6 +11381,13 @@ var ControlClient = class {
11128
11381
  swap(body) {
11129
11382
  return this.request("POST", "/swap", body);
11130
11383
  }
11384
+ httpFetchPaid(body) {
11385
+ return this.request(
11386
+ "POST",
11387
+ "/http-fetch-paid",
11388
+ body
11389
+ );
11390
+ }
11131
11391
  targets() {
11132
11392
  return this.request("GET", "/targets");
11133
11393
  }
@@ -11318,4 +11578,4 @@ export {
11318
11578
  @scure/bip32/lib/esm/index.js:
11319
11579
  (*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
11320
11580
  */
11321
- //# sourceMappingURL=chunk-5KFXUT5Q.js.map
11581
+ //# sourceMappingURL=chunk-CQ2QIZ6Z.js.map