@toon-protocol/client-mcp 0.30.0 → 0.31.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.
Files changed (30) hide show
  1. package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js → anon-proxy-W3KMM7GU-FN7ZJY7P.js} +2 -1
  2. package/dist/chunk-F22GNSF6.js +12 -0
  3. package/dist/{chunk-CH37M2I2.js → chunk-JOGMX4IT.js} +33 -9
  4. package/dist/chunk-JOGMX4IT.js.map +1 -0
  5. package/dist/{chunk-TEMKXTNH.js → chunk-MI2IWKHX.js} +18 -8
  6. package/dist/{chunk-TEMKXTNH.js.map → chunk-MI2IWKHX.js.map} +1 -1
  7. package/dist/chunk-SECSBCDA.js +1567 -0
  8. package/dist/chunk-SECSBCDA.js.map +1 -0
  9. package/dist/daemon.js +3 -2
  10. package/dist/daemon.js.map +1 -1
  11. package/dist/{ed25519-OFFWPWRE.js → ed25519-UB2JDJJK.js} +2 -1
  12. package/dist/{gateway-QOK47RKS-HB65KIKC.js → gateway-QOK47RKS-SEGTXBR3.js} +2 -1
  13. package/dist/{gateway-QOK47RKS-HB65KIKC.js.map → gateway-QOK47RKS-SEGTXBR3.js.map} +1 -1
  14. package/dist/{hmac-7WSXTWW4.js → hmac-26UC6YKX.js} +2 -1
  15. package/dist/hmac-26UC6YKX.js.map +1 -0
  16. package/dist/index.d.ts +72 -9
  17. package/dist/index.js +4 -3
  18. package/dist/mcp.js +3 -2
  19. package/dist/mcp.js.map +1 -1
  20. package/dist/{sha512-LMOIUNFJ.js → sha512-WYC446ZM.js} +2 -1
  21. package/dist/{sha512-LMOIUNFJ.js.map → sha512-WYC446ZM.js.map} +1 -1
  22. package/dist/{socks5-WTJBYGME-IXWLQDE7.js → socks5-WTJBYGME-6COK4LXW.js} +2 -1
  23. package/dist/{socks5-WTJBYGME-IXWLQDE7.js.map → socks5-WTJBYGME-6COK4LXW.js.map} +1 -1
  24. package/package.json +2 -1
  25. package/dist/chunk-2MY6AQK6.js +0 -667
  26. package/dist/chunk-2MY6AQK6.js.map +0 -1
  27. package/dist/chunk-CH37M2I2.js.map +0 -1
  28. /package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js.map → anon-proxy-W3KMM7GU-FN7ZJY7P.js.map} +0 -0
  29. /package/dist/{ed25519-OFFWPWRE.js.map → chunk-F22GNSF6.js.map} +0 -0
  30. /package/dist/{hmac-7WSXTWW4.js.map → ed25519-UB2JDJJK.js.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { NostrEvent } from 'nostr-tools/pure';
2
+ import { SwapPair } from '@toon-protocol/core';
2
3
  import { ToonClientConfig } from '@toon-protocol/client';
3
4
  import { FastifyInstance } from 'fastify';
4
5
 
@@ -116,20 +117,70 @@ interface ChannelInfo {
116
117
  interface ChannelsResponse {
117
118
  channels: ChannelInfo[];
118
119
  }
119
- /** `POST /swap` — pay asset A to a mill peer, receive asset B + a target claim. */
120
+ /**
121
+ * `POST /swap` — pay asset A to a mill peer, receive asset B + a signed
122
+ * target-chain claim. The daemon builds the NIP-59 gift-wrapped kind:20032 swap
123
+ * rumor and streams it via SDK `streamSwap`, signing the source-asset claim
124
+ * against the open apex channel (the mill peer must be routed via
125
+ * `apexChildPeers`).
126
+ */
120
127
  interface SwapRequest {
121
- /** Mill peer ILP destination. */
128
+ /** Mill peer ILP destination (e.g. `g.townhouse.mill`). */
122
129
  destination: string;
123
- /** Amount to send in base units. */
130
+ /** Total source-asset amount to swap, in source micro-units. */
124
131
  amount: string;
125
- /** Optional base64 TOON payload describing the swap intent. */
126
- toonData?: string;
132
+ /** Mill's 64-char lowercase hex Nostr pubkey (NIP-59 gift-wrap recipient). */
133
+ millPubkey: string;
134
+ /**
135
+ * The swap pair to execute — from kind:10032 discovery, or operator-supplied
136
+ * when the mill announces pairs to a relay other than the town relay.
137
+ */
138
+ pair: SwapPair;
139
+ /**
140
+ * Sender's payout address on `pair.to.chain` (EVM 0x-hex / Solana or Mina
141
+ * base58). Echoed on every rumor as the `chain-recipient` tag.
142
+ */
143
+ chainRecipient: string;
144
+ /** Split the swap into N equal packets (default 1). */
145
+ packetCount?: number;
146
+ }
147
+ /** One accumulated, decrypted claim harvested from a single swap packet. */
148
+ interface SwapClaim {
149
+ /** Source-asset amount sent for this packet (micro-units, decimal). */
150
+ sourceAmount: string;
151
+ /** Target-asset amount claimed (micro-units, decimal). */
152
+ targetAmount: string;
153
+ /** Decrypted signed target-chain claim bytes, base64. */
154
+ claim: string;
155
+ /** Target-chain channel id (real on-chain id, or a dev placeholder). */
156
+ channelId?: string;
157
+ /** Sender's payout address echoed by the mill. */
158
+ recipient?: string;
159
+ /** Mill's on-chain signer address. */
160
+ millSignerAddress?: string;
161
+ /** Mill-side claim id. */
162
+ claimId?: string;
163
+ /** Balance-proof nonce on the target channel (decimal). */
164
+ nonce?: string;
165
+ /** Cumulative transferred on the target channel (micro-units, decimal). */
166
+ cumulativeAmount?: string;
127
167
  }
128
168
  interface SwapResponse {
169
+ /** True when at least one packet FULFILLed with a usable claim. */
129
170
  accepted: boolean;
130
- /** base64 FULFILL data (the signed target-chain claim), when accepted. */
131
- data?: string;
171
+ /** Number of packets the mill FULFILLed. */
172
+ packetsAccepted: number;
173
+ /** Per-packet accumulated claims (settlement metadata + signed claim). */
174
+ claims: SwapClaim[];
175
+ /** Total source sent across accepted packets (micro-units, decimal). */
176
+ cumulativeSource: string;
177
+ /** Total target received across accepted packets (micro-units, decimal). */
178
+ cumulativeTarget: string;
179
+ /** Final stream state. */
180
+ state: 'completed' | 'failed' | 'stopped';
181
+ /** First rejection code from the mill, if any (e.g. `F99`). */
132
182
  code?: string;
183
+ /** First rejection message, if any. */
133
184
  message?: string;
134
185
  }
135
186
  /** Uniform error envelope returned with non-2xx responses. */
@@ -571,7 +622,19 @@ declare class ClientRunner {
571
622
  }>;
572
623
  /** List tracked channels with their nonce watermark + cumulative amount. */
573
624
  getChannels(): ChannelsResponse;
574
- /** Send a swap packet to a mill peer. */
625
+ /**
626
+ * Swap source asset → target asset against a mill peer.
627
+ *
628
+ * Uses SDK `streamSwap`, which builds a NIP-59 gift-wrapped kind:20032 swap
629
+ * rumor per packet and sends it over the open BTP session. The source-asset
630
+ * balance proof is signed by the ToonClient's ChannelManager against the apex
631
+ * channel — so the mill peer MUST be routed via `apexChildPeers` (otherwise
632
+ * there is no channel to sign against and the mill rejects with F99).
633
+ *
634
+ * A fresh ephemeral gift-wrap key is generated per swap (used for sealing the
635
+ * rumor AND decrypting the FULFILL claims) — independent of the daemon's
636
+ * settlement identity, so callers never need to expose a key.
637
+ */
575
638
  swap(req: SwapRequest): Promise<SwapResponse>;
576
639
  /** Graceful teardown of the relay subscription + ToonClient. */
577
640
  stop(): Promise<void>;
@@ -662,4 +725,4 @@ declare const TOOL_DEFINITIONS: ToolDefinition[];
662
725
  */
663
726
  declare function dispatchTool(client: ControlClient, name: string, args: Record<string, unknown>): Promise<ToolResult>;
664
727
 
665
- export { type ApexNegotiationConfig, type ChainStatus, type ChannelInfo, type ChannelsResponse, ClientRunner, type ClientRunnerDeps, ControlApiError, ControlClient, type ControlClientOptions, type DaemonConfigFile, DaemonUnreachableError, type DrainResult, type ErrorResponse, type EventsQuery, type EventsResponse, type MinimalWebSocket, type NostrFilter, NotReadyError, type OpenChannelRequest, PublishRejectedError, type PublishRequest, type PublishResponse, RelaySubscription, type RelaySubscriptionOptions, type ResolvedDaemonConfig, type SettlementChain, type StatusResponse, type SubscribeRequest, type SubscribeResponse, type SwapRequest, type SwapResponse, TOOL_DEFINITIONS, type ToolDefinition, type ToolResult, type ToonClientLike, type WebSocketFactory, acquireLock, configDir, defaultConfigPath, dispatchTool, isDaemonRunning, isProcessAlive, readConfigFile, readPid, registerRoutes, releaseLock, resolveConfig, resolveMnemonic, spawnDaemonDetached, waitForReady };
728
+ export { type ApexNegotiationConfig, type ChainStatus, type ChannelInfo, type ChannelsResponse, ClientRunner, type ClientRunnerDeps, ControlApiError, ControlClient, type ControlClientOptions, type DaemonConfigFile, DaemonUnreachableError, type DrainResult, type ErrorResponse, type EventsQuery, type EventsResponse, type MinimalWebSocket, type NostrFilter, NotReadyError, type OpenChannelRequest, PublishRejectedError, type PublishRequest, type PublishResponse, RelaySubscription, type RelaySubscriptionOptions, type ResolvedDaemonConfig, type SettlementChain, type StatusResponse, type SubscribeRequest, type SubscribeResponse, type SwapClaim, type SwapRequest, type SwapResponse, TOOL_DEFINITIONS, type ToolDefinition, type ToolResult, type ToonClientLike, type WebSocketFactory, acquireLock, configDir, defaultConfigPath, dispatchTool, isDaemonRunning, isProcessAlive, readConfigFile, readPid, registerRoutes, releaseLock, resolveConfig, resolveMnemonic, spawnDaemonDetached, waitForReady };
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import {
5
5
  PublishRejectedError,
6
6
  RelaySubscription,
7
7
  registerRoutes
8
- } from "./chunk-2MY6AQK6.js";
8
+ } from "./chunk-SECSBCDA.js";
9
9
  import {
10
10
  TOOL_DEFINITIONS,
11
11
  dispatchTool
12
- } from "./chunk-CH37M2I2.js";
12
+ } from "./chunk-JOGMX4IT.js";
13
13
  import {
14
14
  ControlApiError,
15
15
  ControlClient,
@@ -26,12 +26,13 @@ import {
26
26
  resolveMnemonic,
27
27
  spawnDaemonDetached,
28
28
  waitForReady
29
- } from "./chunk-TEMKXTNH.js";
29
+ } from "./chunk-MI2IWKHX.js";
30
30
  import "./chunk-32QD72IL.js";
31
31
  import "./chunk-QTDCFXPF.js";
32
32
  import "./chunk-LR7W2ISE.js";
33
33
  import "./chunk-VA7XC4FD.js";
34
34
  import "./chunk-SKQTKZIH.js";
35
+ import "./chunk-F22GNSF6.js";
35
36
  export {
36
37
  ClientRunner,
37
38
  ControlApiError,
package/dist/mcp.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
3
3
  import {
4
4
  TOOL_DEFINITIONS,
5
5
  dispatchTool
6
- } from "./chunk-CH37M2I2.js";
6
+ } from "./chunk-JOGMX4IT.js";
7
7
  import {
8
8
  ControlClient,
9
9
  defaultConfigPath,
@@ -11,12 +11,13 @@ import {
11
11
  readConfigFile,
12
12
  spawnDaemonDetached,
13
13
  waitForReady
14
- } from "./chunk-TEMKXTNH.js";
14
+ } from "./chunk-MI2IWKHX.js";
15
15
  import "./chunk-32QD72IL.js";
16
16
  import "./chunk-QTDCFXPF.js";
17
17
  import "./chunk-LR7W2ISE.js";
18
18
  import "./chunk-VA7XC4FD.js";
19
19
  import "./chunk-SKQTKZIH.js";
20
+ import "./chunk-F22GNSF6.js";
20
21
 
21
22
  // src/mcp.ts
22
23
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/dist/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-mcp` — a thin MCP stdio server exposing the TOON client to a Claude\n * agent (Desktop or Code). It holds NO chain keys and NO long-lived\n * connections: every tool maps to an HTTP call against the always-on\n * `toon-clientd` daemon, which it auto-spawns (detached) if it is not running.\n *\n * Works on both surfaces:\n * • Claude Desktop — `claude_desktop_config.json` mcpServers entry.\n * • Claude Code — `claude mcp add toon -- toon-mcp` (or `.mcp.json`).\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolResult,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { ControlClient } from './control-client.js';\nimport { dispatchTool, TOOL_DEFINITIONS } from './mcp-tools.js';\nimport { defaultConfigPath, readConfigFile } from './daemon/config.js';\nimport {\n isDaemonRunning,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\n\n/** stdout carries the MCP protocol — all logging must go to stderr. */\nfunction log(msg: string): void {\n console.error(`[toon-mcp] ${msg}`);\n}\n\n/** Resolve the daemon control-plane URL without needing the mnemonic. */\nfunction controlPlaneUrl(): string {\n const file = readConfigFile(\n process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath()\n );\n const port = Number(\n process.env['TOON_CLIENT_HTTP_PORT'] ?? file.httpPort ?? 8787\n );\n return `http://127.0.0.1:${port}`;\n}\n\n/**\n * Make sure the daemon is up: if the lock shows it running, return; otherwise\n * spawn it detached and wait until the control plane is reachable. Best-effort\n * — failures surface as readable tool errors rather than crashing the server.\n */\nasync function ensureDaemon(url: string): Promise<void> {\n if (isDaemonRunning()) return;\n const client = new ControlClient({ baseUrl: url });\n if (await client.ping()) return;\n log('daemon not running — spawning detached');\n try {\n const pid = spawnDaemonDetached();\n log(`spawned toon-clientd (pid ${pid}); waiting for control plane`);\n await waitForReady(url, 20_000);\n } catch (err) {\n log(\n `failed to spawn daemon: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function main(): Promise<void> {\n const url = controlPlaneUrl();\n const control = new ControlClient({ baseUrl: url });\n\n // Kick off daemon startup; don't block server init on it (anon bootstrap is\n // slow). Tools report \"bootstrapping — retry\" until it is ready.\n void ensureDaemon(url);\n\n const server = new Server(\n { name: 'toon-client', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n // If the daemon went away, try once to bring it back before dispatching.\n if (!(await control.ping())) await ensureDaemon(url);\n // Our ToolResult is a structural subset of CallToolResult (content + isError);\n // the SDK's handler union also carries a task-augmented variant we never use.\n return (await dispatchTool(control, name, args)) as CallToolResult;\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n log(`ready; proxying to ${url}`);\n}\n\nmain().catch((err) => {\n log(err instanceof Error ? (err.stack ?? err.message) : String(err));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAYA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAWP,SAAS,IAAI,KAAmB;AAC9B,UAAQ,MAAM,cAAc,GAAG,EAAE;AACnC;AAGA,SAAS,kBAA0B;AACjC,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,oBAAoB,KAAK,kBAAkB;AAAA,EACzD;AACA,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,uBAAuB,KAAK,KAAK,YAAY;AAAA,EAC3D;AACA,SAAO,oBAAoB,IAAI;AACjC;AAOA,eAAe,aAAa,KAA4B;AACtD,MAAI,gBAAgB,EAAG;AACvB,QAAM,SAAS,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AACjD,MAAI,MAAM,OAAO,KAAK,EAAG;AACzB,MAAI,6CAAwC;AAC5C,MAAI;AACF,UAAM,MAAM,oBAAoB;AAChC,QAAI,6BAA6B,GAAG,8BAA8B;AAClE,UAAM,aAAa,KAAK,GAAM;AAAA,EAChC,SAAS,KAAK;AACZ;AAAA,MACE,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,UAAU,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AAIlD,OAAK,aAAa,GAAG;AAErB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,IACxC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,EAChC;AAEA,SAAO,kBAAkB,wBAAwB,aAAa;AAAA,IAC5D,OAAO;AAAA,EACT,EAAE;AAEF,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,OAAO,QAAQ,OAAO;AAC5B,UAAM,OAAQ,QAAQ,OAAO,aAAa,CAAC;AAE3C,QAAI,CAAE,MAAM,QAAQ,KAAK,EAAI,OAAM,aAAa,GAAG;AAGnD,WAAQ,MAAM,aAAa,SAAS,MAAM,IAAI;AAAA,EAChD,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,sBAAsB,GAAG,EAAE;AACjC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,MAAI,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG,CAAC;AACnE,UAAQ,WAAW;AACrB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-mcp` — a thin MCP stdio server exposing the TOON client to a Claude\n * agent (Desktop or Code). It holds NO chain keys and NO long-lived\n * connections: every tool maps to an HTTP call against the always-on\n * `toon-clientd` daemon, which it auto-spawns (detached) if it is not running.\n *\n * Works on both surfaces:\n * • Claude Desktop — `claude_desktop_config.json` mcpServers entry.\n * • Claude Code — `claude mcp add toon -- toon-mcp` (or `.mcp.json`).\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolResult,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { ControlClient } from './control-client.js';\nimport { dispatchTool, TOOL_DEFINITIONS } from './mcp-tools.js';\nimport { defaultConfigPath, readConfigFile } from './daemon/config.js';\nimport {\n isDaemonRunning,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\n\n/** stdout carries the MCP protocol — all logging must go to stderr. */\nfunction log(msg: string): void {\n console.error(`[toon-mcp] ${msg}`);\n}\n\n/** Resolve the daemon control-plane URL without needing the mnemonic. */\nfunction controlPlaneUrl(): string {\n const file = readConfigFile(\n process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath()\n );\n const port = Number(\n process.env['TOON_CLIENT_HTTP_PORT'] ?? file.httpPort ?? 8787\n );\n return `http://127.0.0.1:${port}`;\n}\n\n/**\n * Make sure the daemon is up: if the lock shows it running, return; otherwise\n * spawn it detached and wait until the control plane is reachable. Best-effort\n * — failures surface as readable tool errors rather than crashing the server.\n */\nasync function ensureDaemon(url: string): Promise<void> {\n if (isDaemonRunning()) return;\n const client = new ControlClient({ baseUrl: url });\n if (await client.ping()) return;\n log('daemon not running — spawning detached');\n try {\n const pid = spawnDaemonDetached();\n log(`spawned toon-clientd (pid ${pid}); waiting for control plane`);\n await waitForReady(url, 20_000);\n } catch (err) {\n log(\n `failed to spawn daemon: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function main(): Promise<void> {\n const url = controlPlaneUrl();\n const control = new ControlClient({ baseUrl: url });\n\n // Kick off daemon startup; don't block server init on it (anon bootstrap is\n // slow). Tools report \"bootstrapping — retry\" until it is ready.\n void ensureDaemon(url);\n\n const server = new Server(\n { name: 'toon-client', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n // If the daemon went away, try once to bring it back before dispatching.\n if (!(await control.ping())) await ensureDaemon(url);\n // Our ToolResult is a structural subset of CallToolResult (content + isError);\n // the SDK's handler union also carries a task-augmented variant we never use.\n return (await dispatchTool(control, name, args)) as CallToolResult;\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n log(`ready; proxying to ${url}`);\n}\n\nmain().catch((err) => {\n log(err instanceof Error ? (err.stack ?? err.message) : String(err));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAYA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAWP,SAAS,IAAI,KAAmB;AAC9B,UAAQ,MAAM,cAAc,GAAG,EAAE;AACnC;AAGA,SAAS,kBAA0B;AACjC,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,oBAAoB,KAAK,kBAAkB;AAAA,EACzD;AACA,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,uBAAuB,KAAK,KAAK,YAAY;AAAA,EAC3D;AACA,SAAO,oBAAoB,IAAI;AACjC;AAOA,eAAe,aAAa,KAA4B;AACtD,MAAI,gBAAgB,EAAG;AACvB,QAAM,SAAS,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AACjD,MAAI,MAAM,OAAO,KAAK,EAAG;AACzB,MAAI,6CAAwC;AAC5C,MAAI;AACF,UAAM,MAAM,oBAAoB;AAChC,QAAI,6BAA6B,GAAG,8BAA8B;AAClE,UAAM,aAAa,KAAK,GAAM;AAAA,EAChC,SAAS,KAAK;AACZ;AAAA,MACE,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,UAAU,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AAIlD,OAAK,aAAa,GAAG;AAErB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,IACxC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,EAChC;AAEA,SAAO,kBAAkB,wBAAwB,aAAa;AAAA,IAC5D,OAAO;AAAA,EACT,EAAE;AAEF,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,OAAO,QAAQ,OAAO;AAC5B,UAAM,OAAQ,QAAQ,OAAO,aAAa,CAAC;AAE3C,QAAI,CAAE,MAAM,QAAQ,KAAK,EAAI,OAAM,aAAa,GAAG;AAGnD,WAAQ,MAAM,aAAa,SAAS,MAAM,IAAI;AAAA,EAChD,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,sBAAsB,GAAG,EAAE;AACjC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,MAAI,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG,CAAC;AACnE,UAAQ,WAAW;AACrB,CAAC;","names":[]}
@@ -10,6 +10,7 @@ import {
10
10
  sha512_256
11
11
  } from "./chunk-LR7W2ISE.js";
12
12
  import "./chunk-VA7XC4FD.js";
13
+ import "./chunk-F22GNSF6.js";
13
14
 
14
15
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/sha512.js
15
16
  var SHA5122 = SHA512;
@@ -30,4 +31,4 @@ export {
30
31
  sha512_2242 as sha512_224,
31
32
  sha512_2562 as sha512_256
32
33
  };
33
- //# sourceMappingURL=sha512-LMOIUNFJ.js.map
34
+ //# sourceMappingURL=sha512-WYC446ZM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/sha512.ts"],"sourcesContent":["/**\n * SHA2-512 a.k.a. sha512 and sha384. It is slower than sha256 in js because u64 operations are slow.\n *\n * Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and\n * [the paper on truncated SHA512/256](https://eprint.iacr.org/2010/548.pdf).\n * @module\n * @deprecated\n */\nimport {\n SHA384 as SHA384n,\n sha384 as sha384n,\n sha512_224 as sha512_224n,\n SHA512_224 as SHA512_224n,\n sha512_256 as sha512_256n,\n SHA512_256 as SHA512_256n,\n SHA512 as SHA512n,\n sha512 as sha512n,\n} from './sha2.ts';\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512: typeof SHA512n = SHA512n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512: typeof sha512n = sha512n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA384: typeof SHA384n = SHA384n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha384: typeof sha384n = sha384n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512_224: typeof SHA512_224n = SHA512_224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512_224: typeof sha512_224n = sha512_224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512_256: typeof SHA512_256n = SHA512_256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512_256: typeof sha512_256n = sha512_256n;\n"],"mappings":";;;;;;;;;;;;;;AAmBO,IAAMA,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;","names":["SHA512","sha512","SHA384","sha384","SHA512_224","sha512_224","SHA512_256","sha512_256"]}
1
+ {"version":3,"sources":["../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/sha512.ts"],"sourcesContent":["/**\n * SHA2-512 a.k.a. sha512 and sha384. It is slower than sha256 in js because u64 operations are slow.\n *\n * Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and\n * [the paper on truncated SHA512/256](https://eprint.iacr.org/2010/548.pdf).\n * @module\n * @deprecated\n */\nimport {\n SHA384 as SHA384n,\n sha384 as sha384n,\n sha512_224 as sha512_224n,\n SHA512_224 as SHA512_224n,\n sha512_256 as sha512_256n,\n SHA512_256 as SHA512_256n,\n SHA512 as SHA512n,\n sha512 as sha512n,\n} from './sha2.ts';\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512: typeof SHA512n = SHA512n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512: typeof sha512n = sha512n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA384: typeof SHA384n = SHA384n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha384: typeof sha384n = sha384n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512_224: typeof SHA512_224n = SHA512_224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512_224: typeof sha512_224n = sha512_224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA512_256: typeof SHA512_256n = SHA512_256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha512_256: typeof sha512_256n = sha512_256n;\n"],"mappings":";;;;;;;;;;;;;;;AAmBO,IAAMA,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,UAAyB;AAE/B,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;AAEvC,IAAMC,cAAiC;","names":["SHA512","sha512","SHA384","sha384","SHA512_224","sha512_224","SHA512_256","sha512_256"]}
@@ -1,4 +1,5 @@
1
1
  import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
+ import "./chunk-F22GNSF6.js";
2
3
 
3
4
  // ../client/dist/socks5-WTJBYGME.js
4
5
  import { createConnection } from "net";
@@ -135,4 +136,4 @@ export {
135
136
  probeSocks5Proxy,
136
137
  validateSocks5hUrl
137
138
  };
138
- //# sourceMappingURL=socks5-WTJBYGME-IXWLQDE7.js.map
139
+ //# sourceMappingURL=socks5-WTJBYGME-6COK4LXW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../client/src/transport/socks5.ts"],"sourcesContent":["/**\n * SOCKS5 transport helpers for Node.js environments.\n *\n * This module is dynamically imported only when `transport.type === 'socks5'`\n * is configured, keeping `ws` and `socks-proxy-agent` out of browser bundles.\n */\n\nimport { createConnection } from 'node:net';\nimport { createRequire } from 'node:module';\nimport type SocksProxyAgentModule from 'socks-proxy-agent';\nimport type WSModule from 'ws';\nimport type httpModule from 'node:http';\nimport type httpsModule from 'node:https';\n\n// ESM-safe require. This module builds as ESM (tsup `format: ['esm']`) with\n// `socks-proxy-agent`/`ws` external, so a bare `require(...)` would be rewritten\n// by esbuild into a `__require` shim that throws\n// `Dynamic require of \"socks-proxy-agent\" is not supported` for any pure-ESM\n// consumer — breaking the SOCKS5/ATOR transport entirely (the npm-consumer\n// toon-client image surfaced this). Building a real require off `import.meta.url`\n// (the same pattern as docker/esbuild.config.mjs's banner) keeps the\n// synchronous, browser-guarded `require(...)` calls below working in the\n// published ESM bundle while leaving the deps external. Browser bundlers never\n// reach this code: the module is dynamically imported only when\n// `transport.type === 'socks5'`, which is Node-only.\nconst require = createRequire(import.meta.url);\n\n/**\n * Parses and validates a `socks5h://` URL.\n * Enforces `socks5h://` scheme (not `socks5://`) to prevent DNS leaks —\n * `.anyone` hostnames must be resolved by the proxy, not locally.\n *\n * Mirrors the connector's `transport/socks-url.ts` validation logic.\n */\nexport function validateSocks5hUrl(socksProxy: string): {\n host: string;\n port: number;\n} {\n if (!socksProxy.startsWith('socks5h://')) {\n throw new Error(\n `SOCKS5 proxy URL must use socks5h:// scheme (got \"${socksProxy.split('://')[0]}://\"). ` +\n 'The \"h\" suffix ensures DNS resolution happens at the proxy, preventing leaks of .anyone hostnames.'\n );\n }\n\n // Parse by converting to http:// for URL constructor compatibility\n const httpUrl = socksProxy.replace(/^socks5h:\\/\\//, 'http://');\n let parsed: URL;\n try {\n parsed = new URL(httpUrl);\n } catch {\n throw new Error(`Malformed SOCKS5 proxy URL: \"${socksProxy}\"`);\n }\n\n const host = parsed.hostname;\n const port = parsed.port ? parseInt(parsed.port, 10) : 1080;\n\n if (!host) {\n throw new Error(`SOCKS5 proxy URL missing host: \"${socksProxy}\"`);\n }\n if (port < 0 || port > 65535 || !Number.isFinite(port)) {\n throw new Error(`SOCKS5 proxy port out of range (0–65535): ${parsed.port}`);\n }\n\n return { host, port };\n}\n\n/**\n * Creates a WebSocket factory that routes connections through a SOCKS5 proxy.\n * Uses the `ws` npm package (Node.js only) which accepts an `agent` option.\n */\nexport function createSocks5WebSocketFactory(\n socksProxy: string\n): (url: string) => WebSocket {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const WS = require('ws') as typeof WSModule;\n\n // 120s timeout: the socks library's default is 30s, which is too short for\n // the ATOR network to build circuits to fresh hidden services from certain\n // network paths (e.g., Akash datacenter → public ATOR proxy → local HS).\n // 120s gives the proxy time to find a working introduction-point circuit.\n const agent = new SocksProxyAgent(socksProxy, { timeout: 120_000 });\n\n // CJS/ESM interop: `require('ws')` can return any of three shapes depending on\n // the bundler/loader: the class directly (pure CJS); `{ default: WSClass, ... }`\n // (esModuleInterop=true / synthetic default); or `{ WebSocket: WSClass, ... }`\n // (named export, no default). Walk the ladder so this factory works in all\n // three environments. Pass 2 code review 2026-05-18: previous `(WS as any).default ?? WS`\n // would accept a namespace object as a \"constructor\" and throw cryptically.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ws = WS as any;\n const WSClass = (typeof ws === 'function'\n ? ws\n : typeof ws.default === 'function'\n ? ws.default\n : typeof ws.WebSocket === 'function'\n ? ws.WebSocket\n : null) as unknown as typeof WSModule.prototype.constructor;\n if (WSClass === null) {\n throw new Error(\n \"createSocks5WebSocketFactory: require('ws') did not yield a constructor on .default, .WebSocket, or the module root.\"\n );\n }\n\n return (url: string) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (WSClass as any)(url, { agent }) as unknown as WebSocket;\n}\n\n/**\n * Creates a fetch wrapper that routes HTTP requests through a SOCKS5 proxy.\n * Uses `socks-proxy-agent` with Node.js `http`/`https` modules (not native\n * fetch, which uses undici and doesn't support SocksProxyAgent's dispatcher).\n */\nexport function createSocks5Fetch(socksProxy: string): typeof fetch {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const http = require('node:http') as typeof httpModule;\n const https = require('node:https') as typeof httpsModule;\n\n const agent = new SocksProxyAgent(socksProxy);\n\n return (input: string | URL | Request, init?: RequestInit) => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n const parsedUrl = new URL(url);\n const isHttps = parsedUrl.protocol === 'https:';\n const transport = isHttps ? https : http;\n\n return new Promise<Response>((resolve, reject) => {\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(\n init.headers instanceof Headers\n ? init.headers.entries()\n : Array.isArray(init.headers)\n ? init.headers\n : Object.entries(init.headers)\n )\n : {};\n\n const req = transport.request(\n url,\n { method, headers, agent, timeout: 30_000 },\n (res) => {\n const chunks: Buffer[] = [];\n res.on('data', (chunk: Buffer) => chunks.push(chunk));\n res.on('end', () => {\n const body = Buffer.concat(chunks);\n const responseHeaders = new Headers();\n for (const [key, val] of Object.entries(res.headers)) {\n if (val)\n responseHeaders.set(\n key,\n Array.isArray(val) ? val.join(', ') : val\n );\n }\n resolve(\n new Response(body, {\n status: res.statusCode ?? 200,\n statusText: res.statusMessage ?? '',\n headers: responseHeaders,\n })\n );\n });\n }\n );\n\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('SOCKS5 proxied request timeout'));\n });\n\n if (init?.signal) {\n init.signal.addEventListener('abort', () => {\n req.destroy();\n reject(new Error('Aborted'));\n });\n }\n\n if (init?.body) {\n req.write(typeof init.body === 'string' ? init.body : init.body);\n }\n req.end();\n });\n };\n}\n\n/**\n * Probes SOCKS5 proxy reachability with a TCP connection test.\n * Fail-closed: throws if the proxy is unreachable within the timeout.\n *\n * @param socksProxy - `socks5h://host:port` URL\n * @param timeoutMs - Connection timeout in milliseconds (default: 2000)\n */\nexport async function probeSocks5Proxy(\n socksProxy: string,\n timeoutMs = 2000\n): Promise<void> {\n const { host, port } = validateSocks5hUrl(socksProxy);\n\n return new Promise<void>((resolve, reject) => {\n const socket = createConnection({ host, port }, () => {\n socket.destroy();\n resolve();\n });\n\n socket.setTimeout(timeoutMs, () => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable (timeout ${timeoutMs}ms). ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n\n socket.on('error', (err) => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable: ${err.message}. ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n });\n}\n"],"mappings":"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","names":["require"]}
1
+ {"version":3,"sources":["../../client/src/transport/socks5.ts"],"sourcesContent":["/**\n * SOCKS5 transport helpers for Node.js environments.\n *\n * This module is dynamically imported only when `transport.type === 'socks5'`\n * is configured, keeping `ws` and `socks-proxy-agent` out of browser bundles.\n */\n\nimport { createConnection } from 'node:net';\nimport { createRequire } from 'node:module';\nimport type SocksProxyAgentModule from 'socks-proxy-agent';\nimport type WSModule from 'ws';\nimport type httpModule from 'node:http';\nimport type httpsModule from 'node:https';\n\n// ESM-safe require. This module builds as ESM (tsup `format: ['esm']`) with\n// `socks-proxy-agent`/`ws` external, so a bare `require(...)` would be rewritten\n// by esbuild into a `__require` shim that throws\n// `Dynamic require of \"socks-proxy-agent\" is not supported` for any pure-ESM\n// consumer — breaking the SOCKS5/ATOR transport entirely (the npm-consumer\n// toon-client image surfaced this). Building a real require off `import.meta.url`\n// (the same pattern as docker/esbuild.config.mjs's banner) keeps the\n// synchronous, browser-guarded `require(...)` calls below working in the\n// published ESM bundle while leaving the deps external. Browser bundlers never\n// reach this code: the module is dynamically imported only when\n// `transport.type === 'socks5'`, which is Node-only.\nconst require = createRequire(import.meta.url);\n\n/**\n * Parses and validates a `socks5h://` URL.\n * Enforces `socks5h://` scheme (not `socks5://`) to prevent DNS leaks —\n * `.anyone` hostnames must be resolved by the proxy, not locally.\n *\n * Mirrors the connector's `transport/socks-url.ts` validation logic.\n */\nexport function validateSocks5hUrl(socksProxy: string): {\n host: string;\n port: number;\n} {\n if (!socksProxy.startsWith('socks5h://')) {\n throw new Error(\n `SOCKS5 proxy URL must use socks5h:// scheme (got \"${socksProxy.split('://')[0]}://\"). ` +\n 'The \"h\" suffix ensures DNS resolution happens at the proxy, preventing leaks of .anyone hostnames.'\n );\n }\n\n // Parse by converting to http:// for URL constructor compatibility\n const httpUrl = socksProxy.replace(/^socks5h:\\/\\//, 'http://');\n let parsed: URL;\n try {\n parsed = new URL(httpUrl);\n } catch {\n throw new Error(`Malformed SOCKS5 proxy URL: \"${socksProxy}\"`);\n }\n\n const host = parsed.hostname;\n const port = parsed.port ? parseInt(parsed.port, 10) : 1080;\n\n if (!host) {\n throw new Error(`SOCKS5 proxy URL missing host: \"${socksProxy}\"`);\n }\n if (port < 0 || port > 65535 || !Number.isFinite(port)) {\n throw new Error(`SOCKS5 proxy port out of range (0–65535): ${parsed.port}`);\n }\n\n return { host, port };\n}\n\n/**\n * Creates a WebSocket factory that routes connections through a SOCKS5 proxy.\n * Uses the `ws` npm package (Node.js only) which accepts an `agent` option.\n */\nexport function createSocks5WebSocketFactory(\n socksProxy: string\n): (url: string) => WebSocket {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const WS = require('ws') as typeof WSModule;\n\n // 120s timeout: the socks library's default is 30s, which is too short for\n // the ATOR network to build circuits to fresh hidden services from certain\n // network paths (e.g., Akash datacenter → public ATOR proxy → local HS).\n // 120s gives the proxy time to find a working introduction-point circuit.\n const agent = new SocksProxyAgent(socksProxy, { timeout: 120_000 });\n\n // CJS/ESM interop: `require('ws')` can return any of three shapes depending on\n // the bundler/loader: the class directly (pure CJS); `{ default: WSClass, ... }`\n // (esModuleInterop=true / synthetic default); or `{ WebSocket: WSClass, ... }`\n // (named export, no default). Walk the ladder so this factory works in all\n // three environments. Pass 2 code review 2026-05-18: previous `(WS as any).default ?? WS`\n // would accept a namespace object as a \"constructor\" and throw cryptically.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ws = WS as any;\n const WSClass = (typeof ws === 'function'\n ? ws\n : typeof ws.default === 'function'\n ? ws.default\n : typeof ws.WebSocket === 'function'\n ? ws.WebSocket\n : null) as unknown as typeof WSModule.prototype.constructor;\n if (WSClass === null) {\n throw new Error(\n \"createSocks5WebSocketFactory: require('ws') did not yield a constructor on .default, .WebSocket, or the module root.\"\n );\n }\n\n return (url: string) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (WSClass as any)(url, { agent }) as unknown as WebSocket;\n}\n\n/**\n * Creates a fetch wrapper that routes HTTP requests through a SOCKS5 proxy.\n * Uses `socks-proxy-agent` with Node.js `http`/`https` modules (not native\n * fetch, which uses undici and doesn't support SocksProxyAgent's dispatcher).\n */\nexport function createSocks5Fetch(socksProxy: string): typeof fetch {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const http = require('node:http') as typeof httpModule;\n const https = require('node:https') as typeof httpsModule;\n\n const agent = new SocksProxyAgent(socksProxy);\n\n return (input: string | URL | Request, init?: RequestInit) => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n const parsedUrl = new URL(url);\n const isHttps = parsedUrl.protocol === 'https:';\n const transport = isHttps ? https : http;\n\n return new Promise<Response>((resolve, reject) => {\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(\n init.headers instanceof Headers\n ? init.headers.entries()\n : Array.isArray(init.headers)\n ? init.headers\n : Object.entries(init.headers)\n )\n : {};\n\n const req = transport.request(\n url,\n { method, headers, agent, timeout: 30_000 },\n (res) => {\n const chunks: Buffer[] = [];\n res.on('data', (chunk: Buffer) => chunks.push(chunk));\n res.on('end', () => {\n const body = Buffer.concat(chunks);\n const responseHeaders = new Headers();\n for (const [key, val] of Object.entries(res.headers)) {\n if (val)\n responseHeaders.set(\n key,\n Array.isArray(val) ? val.join(', ') : val\n );\n }\n resolve(\n new Response(body, {\n status: res.statusCode ?? 200,\n statusText: res.statusMessage ?? '',\n headers: responseHeaders,\n })\n );\n });\n }\n );\n\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('SOCKS5 proxied request timeout'));\n });\n\n if (init?.signal) {\n init.signal.addEventListener('abort', () => {\n req.destroy();\n reject(new Error('Aborted'));\n });\n }\n\n if (init?.body) {\n req.write(typeof init.body === 'string' ? init.body : init.body);\n }\n req.end();\n });\n };\n}\n\n/**\n * Probes SOCKS5 proxy reachability with a TCP connection test.\n * Fail-closed: throws if the proxy is unreachable within the timeout.\n *\n * @param socksProxy - `socks5h://host:port` URL\n * @param timeoutMs - Connection timeout in milliseconds (default: 2000)\n */\nexport async function probeSocks5Proxy(\n socksProxy: string,\n timeoutMs = 2000\n): Promise<void> {\n const { host, port } = validateSocks5hUrl(socksProxy);\n\n return new Promise<void>((resolve, reject) => {\n const socket = createConnection({ host, port }, () => {\n socket.destroy();\n resolve();\n });\n\n socket.setTimeout(timeoutMs, () => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable (timeout ${timeoutMs}ms). ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n\n socket.on('error', (err) => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable: ${err.message}. ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n });\n}\n"],"mappings":"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","names":["require"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/client-mcp",
3
- "version": "0.30.0",
3
+ "version": "0.31.1",
4
4
  "description": "Always-on local daemon + MCP server letting a Claude agent (Desktop or Code) act as a TOON Protocol client: pay-to-write publishing, free reads, channel/balance management, and mill swaps.",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Green",
@@ -45,6 +45,7 @@
45
45
  "typescript": "^5.3.0",
46
46
  "vitest": "^1.0.0",
47
47
  "@toon-protocol/client": "0.9.1",
48
+ "@toon-protocol/sdk": "0.5.0",
48
49
  "@toon-protocol/core": "1.4.1"
49
50
  },
50
51
  "engines": {