@toon-protocol/client-mcp 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6831,26 +6831,49 @@ function getNetworkStatus(config) {
6831
6831
  if (!network || network === "custom") return void 0;
6832
6832
  return resolveClientNetwork(network).status;
6833
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
+ }
6834
6839
  function validateConfig(config) {
6835
6840
  if (config.connector !== void 0) {
6836
6841
  throw new ValidationError(
6837
6842
  "Embedded mode not yet implemented in ToonClient. Use connectorUrl for HTTP mode."
6838
6843
  );
6839
6844
  }
6840
- if (!config.connectorUrl) {
6845
+ if (!config.connectorUrl && !config.proxyUrl) {
6841
6846
  throw new ValidationError(
6842
- 'connectorUrl is required for HTTP mode. Example: "http://localhost:8080"'
6847
+ 'connectorUrl (or proxyUrl) is required for HTTP mode. Example: "http://localhost:8080"'
6843
6848
  );
6844
6849
  }
6845
- try {
6846
- const url = new URL(config.connectorUrl);
6847
- if (!url.protocol.startsWith("http")) {
6848
- 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
+ );
6849
6876
  }
6850
- } catch (error) {
6851
- throw new ValidationError(
6852
- `Invalid connectorUrl: must be a valid HTTP/HTTPS URL (e.g., "http://localhost:8080"). Error: ${error instanceof Error ? error.message : String(error)}`
6853
- );
6854
6877
  }
6855
6878
  if (config.secretKey !== void 0) {
6856
6879
  if (!config.secretKey || config.secretKey.length !== 32) {
@@ -6914,25 +6937,6 @@ function validateConfig(config) {
6914
6937
  );
6915
6938
  }
6916
6939
  }
6917
- if (config.transport) {
6918
- if (config.transport.type === "socks5") {
6919
- if (!config.transport.socksProxy?.startsWith("socks5h://")) {
6920
- throw new ValidationError(
6921
- 'transport.socksProxy must use socks5h:// scheme to prevent DNS leaks. The "h" suffix ensures .anyone hostnames are resolved by the proxy, not locally.'
6922
- );
6923
- }
6924
- } else if (config.transport.type === "gateway") {
6925
- if (!config.transport.gatewayUrl) {
6926
- throw new ValidationError(
6927
- "transport.gatewayUrl is required for gateway transport"
6928
- );
6929
- }
6930
- } else if (config.transport.type !== "direct") {
6931
- throw new ValidationError(
6932
- `Unknown transport type: "${config.transport.type}"`
6933
- );
6934
- }
6935
- }
6936
6940
  if (config.chainRpcUrls && config.supportedChains) {
6937
6941
  for (const chain2 of Object.keys(config.chainRpcUrls)) {
6938
6942
  if (!config.supportedChains.includes(chain2)) {
@@ -6949,19 +6953,21 @@ function applyDefaults(rawConfig) {
6949
6953
  config.mnemonic,
6950
6954
  config.mnemonicAccountIndex ?? 0
6951
6955
  ).secretKey : generateSecretKey2());
6956
+ const connectorHttpEndpoint = config.connectorHttpEndpoint ?? proxyIlpEndpoint(config.proxyUrl);
6957
+ const connectorUrl = config.connectorUrl ?? config.proxyUrl?.replace(/\/+$/, "");
6952
6958
  let btpUrl = config.btpUrl;
6953
- if (!btpUrl && config.connectorUrl) {
6959
+ if (!btpUrl && connectorUrl && !connectorHttpEndpoint) {
6954
6960
  try {
6955
- const url = new URL(config.connectorUrl);
6961
+ const url = new URL(connectorUrl);
6956
6962
  const wsProtocol = url.protocol === "https:" ? "wss:" : "ws:";
6957
6963
  btpUrl = `${wsProtocol}//${url.hostname}:3000`;
6958
6964
  } catch {
6959
6965
  }
6960
6966
  }
6961
6967
  let destinationAddress = config.destinationAddress;
6962
- if (!destinationAddress && config.connectorUrl) {
6968
+ if (!destinationAddress && connectorUrl) {
6963
6969
  try {
6964
- const url = new URL(config.connectorUrl);
6970
+ const url = new URL(connectorUrl);
6965
6971
  if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
6966
6972
  if (url.port === "8080") {
6967
6973
  destinationAddress = "g.toon.genesis";
@@ -6984,8 +6990,10 @@ function applyDefaults(rawConfig) {
6984
6990
  ...config,
6985
6991
  secretKey,
6986
6992
  evmPrivateKey,
6987
- connectorUrl: config.connectorUrl,
6988
- // 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 } : {},
6989
6997
  relayUrl: config.relayUrl ?? "ws://localhost:7100",
6990
6998
  queryTimeout: config.queryTimeout ?? 3e4,
6991
6999
  maxRetries: config.maxRetries ?? 3,
@@ -7045,6 +7053,13 @@ function decodeUtf8(bytes) {
7045
7053
  function isBase64(str) {
7046
7054
  return /^[A-Za-z0-9+/]*={0,2}$/.test(str);
7047
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
+ }
7048
7063
  async function withRetry(operation, options) {
7049
7064
  const {
7050
7065
  maxRetries,
@@ -9219,79 +9234,9 @@ var EvmSigner = class {
9219
9234
  };
9220
9235
  }
9221
9236
  };
9222
- function isAnyoneHost(url) {
9223
- if (!url) return false;
9224
- try {
9225
- const withScheme = /:\/\//.test(url) ? url : `ws://${url}`;
9226
- const host = new URL(withScheme).hostname.toLowerCase();
9227
- return host.endsWith(".anyone");
9228
- } catch {
9229
- return false;
9230
- }
9231
- }
9232
- async function resolveTransport(transport, originalBtpUrl, originalConnectorUrl, managedProxyOptions) {
9233
- const hasExplicitProxy = !!transport && (transport.type === "socks5" || transport.type === "gateway");
9234
- const envProxy = process.env["ANYONE_PROXY_URLS"];
9235
- if (!hasExplicitProxy && managedProxyOptions?.managedAnonProxy !== false && !envProxy && isAnyoneHost(originalBtpUrl)) {
9236
- const { startManagedAnonProxy: startManagedAnonProxy2 } = await import("./anon-proxy-W3KMM7GU-FN7ZJY7P.js");
9237
- const { createSocks5WebSocketFactory, createSocks5Fetch } = await import("./socks5-WTJBYGME-6COK4LXW.js");
9238
- const proxy = await startManagedAnonProxy2({
9239
- ...managedProxyOptions?.managedAnonSocksPort !== void 0 ? { socksPort: managedProxyOptions.managedAnonSocksPort } : {}
9240
- });
9241
- try {
9242
- return {
9243
- createWebSocket: createSocks5WebSocketFactory(proxy.socksProxy),
9244
- httpClient: createSocks5Fetch(proxy.socksProxy),
9245
- stopManagedProxy: proxy.stop
9246
- };
9247
- } catch (err) {
9248
- await proxy.stop();
9249
- throw err;
9250
- }
9251
- }
9252
- if (!transport || transport.type === "direct") {
9253
- return {};
9254
- }
9255
- if (transport.type === "socks5") {
9256
- const {
9257
- createSocks5WebSocketFactory,
9258
- createSocks5Fetch,
9259
- probeSocks5Proxy
9260
- } = await import("./socks5-WTJBYGME-6COK4LXW.js");
9261
- await probeSocks5Proxy(transport.socksProxy);
9262
- return {
9263
- createWebSocket: createSocks5WebSocketFactory(transport.socksProxy),
9264
- httpClient: createSocks5Fetch(transport.socksProxy)
9265
- };
9266
- }
9267
- if (transport.type === "gateway") {
9268
- const { rewriteUrlsForGateway } = await import("./gateway-QOK47RKS-SEGTXBR3.js");
9269
- const rewritten = rewriteUrlsForGateway(
9270
- transport.gatewayUrl,
9271
- originalBtpUrl,
9272
- originalConnectorUrl
9273
- );
9274
- return {
9275
- btpUrl: rewritten.btpUrl,
9276
- connectorUrl: rewritten.connectorUrl
9277
- };
9278
- }
9279
- throw new Error(
9280
- `Unknown transport type: "${transport.type}"`
9281
- );
9282
- }
9283
9237
  async function initializeHttpMode(config) {
9284
- const transport = await resolveTransport(
9285
- config.transport,
9286
- config.btpUrl,
9287
- config.connectorUrl,
9288
- {
9289
- ...config.managedAnonProxy !== void 0 ? { managedAnonProxy: config.managedAnonProxy } : {},
9290
- ...config.managedAnonSocksPort !== void 0 ? { managedAnonSocksPort: config.managedAnonSocksPort } : {}
9291
- }
9292
- );
9293
- const effectiveBtpUrl = transport.btpUrl ?? config.btpUrl;
9294
- const effectiveConnectorUrl = transport.connectorUrl ?? config.connectorUrl;
9238
+ const effectiveBtpUrl = config.btpUrl;
9239
+ const effectiveConnectorUrl = config.connectorUrl;
9295
9240
  const settlementInfo = buildSettlementInfo(config);
9296
9241
  const discoveredPeer = readDiscoveredIlpPeer({
9297
9242
  btpEndpoint: effectiveBtpUrl,
@@ -9304,8 +9249,7 @@ async function initializeHttpMode(config) {
9304
9249
  btpClient = new BtpRuntimeClient({
9305
9250
  btpUrl: effectiveBtpUrl,
9306
9251
  peerId: config.btpPeerId ?? `client`,
9307
- authToken: config.btpAuthToken ?? "",
9308
- createWebSocket: transport.createWebSocket
9252
+ authToken: config.btpAuthToken ?? ""
9309
9253
  });
9310
9254
  await btpClient.connect();
9311
9255
  }
@@ -9317,17 +9261,14 @@ async function initializeHttpMode(config) {
9317
9261
  ...config.btpAuthToken !== void 0 ? { authToken: config.btpAuthToken } : {},
9318
9262
  timeout: config.queryTimeout,
9319
9263
  maxRetries: config.maxRetries,
9320
- retryDelay: config.retryDelay,
9321
- ...transport.httpClient !== void 0 ? { httpClient: transport.httpClient } : {},
9322
- ...transport.createWebSocket !== void 0 ? { createWebSocket: transport.createWebSocket } : {}
9264
+ retryDelay: config.retryDelay
9323
9265
  });
9324
9266
  }
9325
9267
  const runtimeClient = httpIlpClient ?? btpClient ?? new HttpRuntimeClient({
9326
9268
  connectorUrl: effectiveConnectorUrl,
9327
9269
  timeout: config.queryTimeout,
9328
9270
  maxRetries: config.maxRetries,
9329
- retryDelay: config.retryDelay,
9330
- httpClient: transport.httpClient
9271
+ retryDelay: config.retryDelay
9331
9272
  });
9332
9273
  let onChainChannelClient = null;
9333
9274
  if (config.chainRpcUrls) {
@@ -9372,10 +9313,7 @@ async function initializeHttpMode(config) {
9372
9313
  runtimeClient,
9373
9314
  adminClient: null,
9374
9315
  btpClient,
9375
- onChainChannelClient,
9376
- // Teardown handle for a managed `anon` proxy this init STARTED (undefined
9377
- // for explicit-proxy/direct/gateway). ToonClient.stop() invokes it.
9378
- stopManagedProxy: transport.stopManagedProxy
9316
+ onChainChannelClient
9379
9317
  };
9380
9318
  }
9381
9319
  var SolanaSigner = class {
@@ -10514,13 +10452,7 @@ var ToonClient = class {
10514
10452
  }
10515
10453
  }
10516
10454
  const initialization = await initializeHttpMode(this.config);
10517
- const {
10518
- bootstrapService,
10519
- discoveryTracker,
10520
- runtimeClient,
10521
- btpClient,
10522
- stopManagedProxy
10523
- } = initialization;
10455
+ const { bootstrapService, discoveryTracker, runtimeClient, btpClient } = initialization;
10524
10456
  if (this.channelManager) {
10525
10457
  const cm = this.channelManager;
10526
10458
  const nostrPubkey = this.getPublicKey();
@@ -10608,8 +10540,7 @@ var ToonClient = class {
10608
10540
  discoveryTracker,
10609
10541
  runtimeClient,
10610
10542
  peersDiscovered: bootstrapResults.length,
10611
- btpClient: btpClient ?? void 0,
10612
- ...stopManagedProxy ? { stopManagedProxy } : {}
10543
+ btpClient: btpClient ?? void 0
10613
10544
  };
10614
10545
  return {
10615
10546
  peersDiscovered: bootstrapResults.length,
@@ -10645,13 +10576,9 @@ var ToonClient = class {
10645
10576
  const toonData = this.config.toonEncoder(event);
10646
10577
  const basePricePerByte = 10n;
10647
10578
  const amount = options?.ilpAmount !== void 0 ? String(options.ilpAmount) : String(BigInt(toonData.length) * basePricePerByte);
10579
+ const writeData = buildStoreWriteEnvelope(event);
10648
10580
  const destination = options?.destination ?? this.config.destinationAddress;
10649
- if (!this.state.btpClient) {
10650
- throw new ToonClientError(
10651
- "BTP client required for publishing. Configure btpUrl.",
10652
- "NO_BTP_CLIENT"
10653
- );
10654
- }
10581
+ const transport = this.getClaimTransport();
10655
10582
  let claimMessage;
10656
10583
  if (options?.claim) {
10657
10584
  claimMessage = this.buildClaimMessageForProof(options.claim);
@@ -10680,13 +10607,11 @@ var ToonClient = class {
10680
10607
  "MISSING_CLAIM"
10681
10608
  );
10682
10609
  }
10683
- const response = await this.state.btpClient.sendIlpPacketWithClaim(
10610
+ const response = await transport.sendIlpPacketWithClaim(
10684
10611
  {
10685
10612
  destination,
10686
10613
  amount,
10687
- data: toBase64(
10688
- toonData instanceof Uint8Array ? toonData : new Uint8Array(toonData)
10689
- )
10614
+ data: toBase64(writeData)
10690
10615
  },
10691
10616
  claimMessage
10692
10617
  );
@@ -10766,7 +10691,7 @@ var ToonClient = class {
10766
10691
  * matching `destination`,
10767
10692
  * (c) neither -> throw MISSING_CLAIM.
10768
10693
  *
10769
- * @throws {ToonClientError} INVALID_STATE / NO_BTP_CLIENT / MISSING_CLAIM
10694
+ * @throws {ToonClientError} INVALID_STATE / NO_ILP_TRANSPORT / MISSING_CLAIM
10770
10695
  */
10771
10696
  async sendSwapPacket(params) {
10772
10697
  if (!this.state) {
@@ -10775,18 +10700,13 @@ var ToonClient = class {
10775
10700
  "INVALID_STATE"
10776
10701
  );
10777
10702
  }
10778
- if (!this.state.btpClient) {
10779
- throw new ToonClientError(
10780
- "BTP client required for sending swap packets. Configure btpUrl.",
10781
- "NO_BTP_CLIENT"
10782
- );
10783
- }
10703
+ const transport = this.getClaimTransport();
10784
10704
  const claimMessage = await this.resolveClaimForDestination(
10785
10705
  params.destination,
10786
10706
  params.amount,
10787
10707
  params.claim
10788
10708
  );
10789
- return this.state.btpClient.sendIlpPacketWithClaim(
10709
+ return transport.sendIlpPacketWithClaim(
10790
10710
  {
10791
10711
  destination: params.destination,
10792
10712
  amount: String(params.amount),
@@ -10826,6 +10746,51 @@ var ToonClient = class {
10826
10746
  }
10827
10747
  return EvmSigner.buildClaimMessage(claim, this.getPublicKey());
10828
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
+ }
10829
10794
  /**
10830
10795
  * Shared claim-resolution logic used by `publishEvent` and `sendSwapPacket`.
10831
10796
  * TODO(12.5 followup): also factor `publishEvent`'s inline claim resolution
@@ -11014,14 +10979,9 @@ var ToonClient = class {
11014
10979
  "MISSING_CLAIM"
11015
10980
  );
11016
10981
  }
11017
- if (!this.state.btpClient) {
11018
- throw new ToonClientError(
11019
- "BTP client required for sending payments. Configure btpUrl.",
11020
- "NO_BTP_CLIENT"
11021
- );
11022
- }
10982
+ const transport = this.getClaimTransport();
11023
10983
  const claimMessage = this.buildClaimMessageForProof(params.claim);
11024
- return this.state.btpClient.sendIlpPacketWithClaim(
10984
+ return transport.sendIlpPacketWithClaim(
11025
10985
  ilpParams,
11026
10986
  claimMessage
11027
10987
  );
@@ -11039,14 +10999,10 @@ var ToonClient = class {
11039
10999
  if (!this.state) {
11040
11000
  throw new ToonClientError("Client not started", "INVALID_STATE");
11041
11001
  }
11042
- const stopManagedProxy = this.state.stopManagedProxy;
11043
11002
  try {
11044
11003
  if (this.state.btpClient) {
11045
11004
  await this.state.btpClient.disconnect();
11046
11005
  }
11047
- if (stopManagedProxy) {
11048
- await stopManagedProxy();
11049
- }
11050
11006
  this.state = null;
11051
11007
  } catch (error) {
11052
11008
  throw new ToonClientError(
@@ -11249,71 +11205,40 @@ function resolveMnemonic(file) {
11249
11205
  }
11250
11206
  function resolveConfig(file) {
11251
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;
11252
11210
  const btpUrl = process.env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl;
11253
- if (!btpUrl) {
11254
- throw new Error(
11255
- "No btpUrl configured. Set TOON_CLIENT_BTP_URL or add `btpUrl` to the config file."
11256
- );
11257
- }
11211
+ const hasUplink = Boolean(btpUrl || proxyUrl);
11258
11212
  const relayUrl = process.env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl ?? "ws://localhost:7100";
11259
- const explicitSocks = process.env["TOON_CLIENT_SOCKS"] ?? file.socksProxy;
11260
11213
  const httpPort = Number(
11261
11214
  process.env["TOON_CLIENT_HTTP_PORT"] ?? file.httpPort ?? 8787
11262
11215
  );
11263
- const destination = file.destination ?? "g.townhouse.town";
11216
+ const destination = process.env["TOON_CLIENT_DESTINATION"] ?? file.destination ?? "g.townhouse.town";
11264
11217
  const feePerEvent = BigInt(file.feePerEvent ?? "1");
11265
11218
  const network = process.env["TOON_CLIENT_NETWORK"] ?? file.network;
11266
11219
  const chain2 = process.env["TOON_CLIENT_CHAIN"] ?? file.chain ?? "evm";
11267
- const apex = file.apexChains?.[chain2] ?? file.apex;
11268
- const managedPort = Number(file.managedAnonSocksPort ?? 9050);
11269
- const allowManaged = file.managedAnonProxy ?? true;
11270
- const btpIsAnyone = isAnyoneHost2(btpUrl);
11271
- const relayIsAnyone = isAnyoneHost2(relayUrl);
11272
- let transport;
11273
- let managedAnonProxy;
11274
- let managedAnonSocksPort;
11275
- let readsSocksProxy;
11276
- let manageReadProxy = false;
11277
- if (explicitSocks) {
11278
- transport = { type: "socks5", socksProxy: explicitSocks };
11279
- managedAnonProxy = false;
11280
- readsSocksProxy = explicitSocks;
11281
- } else if (allowManaged && btpIsAnyone) {
11282
- transport = { type: "direct" };
11283
- managedAnonProxy = true;
11284
- managedAnonSocksPort = managedPort;
11285
- readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
11286
- } else if (allowManaged && relayIsAnyone) {
11287
- transport = { type: "direct" };
11288
- managedAnonProxy = false;
11289
- manageReadProxy = true;
11290
- managedAnonSocksPort = managedPort;
11291
- readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
11292
- } else {
11293
- transport = { type: "direct" };
11294
- managedAnonProxy = false;
11295
- }
11220
+ const apex = file.apexChains?.[chain2] ?? file.apex ?? buildProxyApexNegotiation(file, chain2, destination);
11296
11221
  const channelStorePath = file.channelStorePath ?? join2(configDir(), "channels.json");
11297
11222
  const apexChannelStorePath = join2(configDir(), "apex-channels.json");
11298
11223
  const toonClientConfig = {
11299
- // Required by validateConfig but unused at runtime (BTP transport is used).
11300
- 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 } : {},
11301
11230
  mnemonic,
11302
11231
  mnemonicAccountIndex: file.mnemonicAccountIndex ?? 0,
11303
11232
  ilpInfo: {
11304
11233
  pubkey: "00".repeat(32),
11305
11234
  ilpAddress: "g.toon.client",
11306
- btpEndpoint: btpUrl,
11235
+ btpEndpoint: btpUrl ?? "",
11307
11236
  assetCode: "USD",
11308
11237
  assetScale: 6
11309
11238
  },
11310
11239
  toonEncoder: encodeEventToToon,
11311
11240
  toonDecoder: decodeEventFromToon,
11312
- btpUrl,
11313
- btpAuthToken: "",
11314
- transport,
11315
- managedAnonProxy,
11316
- ...managedAnonSocksPort !== void 0 ? { managedAnonSocksPort } : {},
11241
+ ...btpUrl ? { btpUrl, btpAuthToken: "" } : {},
11317
11242
  destinationAddress: destination,
11318
11243
  relayUrl: "",
11319
11244
  // reads use our own RelaySubscription, not bootstrap discovery
@@ -11331,12 +11256,12 @@ function resolveConfig(file) {
11331
11256
  return {
11332
11257
  httpPort,
11333
11258
  relayUrl,
11334
- ...readsSocksProxy !== void 0 ? { socksProxy: readsSocksProxy } : {},
11335
- manageReadProxy,
11336
- ...manageReadProxy ? { readProxySocksPort: managedPort } : {},
11259
+ hasUplink,
11260
+ ...proxyUrl ? { proxyUrl } : {},
11261
+ ...faucetUrl ? { faucetUrl } : {},
11337
11262
  destination,
11338
11263
  feePerEvent,
11339
- apex,
11264
+ ...apex ? { apex } : {},
11340
11265
  ...file.apexChildPeers ? { apexChildPeers: file.apexChildPeers } : {},
11341
11266
  chain: chain2,
11342
11267
  apexChannelStorePath,
@@ -11344,12 +11269,38 @@ function resolveConfig(file) {
11344
11269
  network
11345
11270
  };
11346
11271
  }
11347
- function isAnyoneHost2(url) {
11348
- try {
11349
- return new URL(url).hostname.endsWith(".anyone");
11350
- } catch {
11351
- return false;
11352
- }
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
+ };
11353
11304
  }
11354
11305
 
11355
11306
  // src/control-client.ts
@@ -11627,4 +11578,4 @@ export {
11627
11578
  @scure/bip32/lib/esm/index.js:
11628
11579
  (*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
11629
11580
  */
11630
- //# sourceMappingURL=chunk-NP35YO5O.js.map
11581
+ //# sourceMappingURL=chunk-CQ2QIZ6Z.js.map