@toon-protocol/client-mcp 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js → anon-proxy-W3KMM7GU-FN7ZJY7P.js} +2 -1
  2. package/dist/chunk-4K6S3VPM.js +1567 -0
  3. package/dist/chunk-4K6S3VPM.js.map +1 -0
  4. package/dist/chunk-F22GNSF6.js +12 -0
  5. package/dist/{chunk-TEMKXTNH.js → chunk-QHNSETFB.js} +18 -8
  6. package/dist/{chunk-TEMKXTNH.js.map → chunk-QHNSETFB.js.map} +1 -1
  7. package/dist/{chunk-CH37M2I2.js → chunk-U227QSQG.js} +33 -9
  8. package/dist/chunk-U227QSQG.js.map +1 -0
  9. package/dist/daemon.js +3 -2
  10. package/dist/daemon.js.map +1 -1
  11. package/dist/{ed25519-OFFWPWRE.js → ed25519-UB2JDJJK.js} +2 -1
  12. package/dist/{gateway-QOK47RKS-HB65KIKC.js → gateway-QOK47RKS-SEGTXBR3.js} +2 -1
  13. package/dist/{gateway-QOK47RKS-HB65KIKC.js.map → gateway-QOK47RKS-SEGTXBR3.js.map} +1 -1
  14. package/dist/{hmac-7WSXTWW4.js → hmac-26UC6YKX.js} +2 -1
  15. package/dist/hmac-26UC6YKX.js.map +1 -0
  16. package/dist/index.d.ts +72 -9
  17. package/dist/index.js +4 -3
  18. package/dist/mcp.js +3 -2
  19. package/dist/mcp.js.map +1 -1
  20. package/dist/{sha512-LMOIUNFJ.js → sha512-WYC446ZM.js} +2 -1
  21. package/dist/{sha512-LMOIUNFJ.js.map → sha512-WYC446ZM.js.map} +1 -1
  22. package/dist/{socks5-WTJBYGME-IXWLQDE7.js → socks5-WTJBYGME-6COK4LXW.js} +2 -1
  23. package/dist/{socks5-WTJBYGME-IXWLQDE7.js.map → socks5-WTJBYGME-6COK4LXW.js.map} +1 -1
  24. package/package.json +3 -2
  25. package/dist/chunk-2MY6AQK6.js +0 -667
  26. package/dist/chunk-2MY6AQK6.js.map +0 -1
  27. package/dist/chunk-CH37M2I2.js.map +0 -1
  28. /package/dist/{anon-proxy-W3KMM7GU-L5IBCUAX.js.map → anon-proxy-W3KMM7GU-FN7ZJY7P.js.map} +0 -0
  29. /package/dist/{ed25519-OFFWPWRE.js.map → chunk-F22GNSF6.js.map} +0 -0
  30. /package/dist/{hmac-7WSXTWW4.js.map → ed25519-UB2JDJJK.js.map} +0 -0
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
2
2
  import {
3
3
  ControlApiError,
4
4
  DaemonUnreachableError
5
- } from "./chunk-TEMKXTNH.js";
5
+ } from "./chunk-QHNSETFB.js";
6
6
 
7
7
  // src/mcp-tools.ts
8
8
  var TOOL_DEFINITIONS = [
@@ -106,21 +106,42 @@ var TOOL_DEFINITIONS = [
106
106
  },
107
107
  {
108
108
  name: "toon_swap",
109
- description: "Pay a mill peer (asset A) to receive asset B plus a signed target-chain claim. Returns the FULFILL data when accepted.",
109
+ description: "Pay a mill peer (asset A) to receive asset B plus a signed target-chain claim. Builds the NIP-59 gift-wrapped kind:20032 swap rumor and streams it; the source-asset claim is signed against the open apex channel (the mill must be routed via apexChildPeers). Returns the accumulated, decrypted target-chain claim(s) and settlement metadata.",
110
110
  inputSchema: {
111
111
  type: "object",
112
112
  properties: {
113
113
  destination: {
114
114
  type: "string",
115
- description: "Mill peer ILP destination."
115
+ description: "Mill peer ILP destination (e.g. g.townhouse.mill)."
116
116
  },
117
- amount: { type: "string", description: "Amount to send, base units." },
118
- toonData: {
117
+ amount: {
119
118
  type: "string",
120
- description: "Optional base64 TOON payload."
119
+ description: "Total source-asset amount to swap, source micro-units."
120
+ },
121
+ millPubkey: {
122
+ type: "string",
123
+ description: "Mill's 64-char lowercase hex Nostr pubkey (gift-wrap recipient)."
124
+ },
125
+ pair: {
126
+ type: "object",
127
+ description: "The swap pair (from kind:10032 discovery or operator-supplied): { from:{assetCode,assetScale,chain}, to:{...}, rate, minAmount?, maxAmount? }."
128
+ },
129
+ chainRecipient: {
130
+ type: "string",
131
+ description: "Sender's payout address on pair.to.chain (EVM 0x-hex / Solana / Mina base58)."
132
+ },
133
+ packetCount: {
134
+ type: "number",
135
+ description: "Split the swap into N equal packets (default 1)."
121
136
  }
122
137
  },
123
- required: ["destination", "amount"],
138
+ required: [
139
+ "destination",
140
+ "amount",
141
+ "millPubkey",
142
+ "pair",
143
+ "chainRecipient"
144
+ ],
124
145
  additionalProperties: false
125
146
  }
126
147
  }
@@ -168,7 +189,10 @@ async function dispatchTool(client, name, args) {
168
189
  await client.swap({
169
190
  destination: String(args["destination"]),
170
191
  amount: String(args["amount"]),
171
- ...typeof args["toonData"] === "string" ? { toonData: args["toonData"] } : {}
192
+ millPubkey: String(args["millPubkey"]),
193
+ pair: args["pair"],
194
+ chainRecipient: String(args["chainRecipient"]),
195
+ ...typeof args["packetCount"] === "number" ? { packetCount: args["packetCount"] } : {}
172
196
  })
173
197
  );
174
198
  default:
@@ -202,4 +226,4 @@ export {
202
226
  TOOL_DEFINITIONS,
203
227
  dispatchTool
204
228
  };
205
- //# sourceMappingURL=chunk-CH37M2I2.js.map
229
+ //# sourceMappingURL=chunk-U227QSQG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp-tools.ts"],"sourcesContent":["/**\n * MCP tool definitions + dispatch. The MCP server is a thin proxy: each tool\n * maps to a `toon-clientd` control-plane call. This module is the testable core\n * (no stdio / SDK transport) so the tool→HTTP mapping and the\n * \"bootstrapping — retry\" handling can be unit-tested directly.\n */\n\nimport { ControlApiError, DaemonUnreachableError } from './control-client.js';\nimport type { ControlClient } from './control-client.js';\nimport type {\n NostrFilter,\n PublishRequest,\n SwapRequest,\n} from './control-api.js';\n\n/** A JSON-Schema-described MCP tool. */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\n/** MCP tool-call result shape (subset of the SDK's CallToolResult). */\nexport interface ToolResult {\n content: { type: 'text'; text: string }[];\n isError?: boolean;\n}\n\nexport const TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n name: 'toon_status',\n description:\n 'Report TOON client daemon health: bootstrapping/ready state, transport, ' +\n 'relay connection, buffered-event count, and per-chain settlement status.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_identity',\n description:\n \"Return this client's public identity (Nostr pubkey + EVM/Solana/Mina \" +\n 'addresses). Never returns private keys.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_publish',\n description:\n 'Pay-to-write: publish a fully-signed Nostr event to the TOON network. ' +\n 'Signs an off-chain payment-channel claim and forwards it over BTP. ' +\n 'Returns the event id, channel id, and the advanced channel nonce.',\n inputSchema: {\n type: 'object',\n properties: {\n event: {\n type: 'object',\n description:\n 'A fully-signed Nostr event (must include id, pubkey, sig, kind, ' +\n 'created_at, tags, content).',\n },\n destination: {\n type: 'string',\n description:\n 'Optional ILP destination override (default: the apex/town).',\n },\n fee: {\n type: 'string',\n description:\n 'Optional fee override in base units (default: daemon config).',\n },\n },\n required: ['event'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_subscribe',\n description:\n 'Free read: register a persistent town-relay subscription with NIP-01 ' +\n 'filter(s). Returns a subscription id to drain with toon_read.',\n inputSchema: {\n type: 'object',\n properties: {\n filters: {\n description: 'A NIP-01 filter object or an array of OR-ed filters.',\n },\n subId: { type: 'string', description: 'Optional caller-supplied id.' },\n },\n required: ['filters'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_read',\n description:\n 'Free read: drain buffered events newer than a cursor. Pass back the ' +\n 'returned cursor to fetch only events received since the last read.',\n inputSchema: {\n type: 'object',\n properties: {\n subId: { type: 'string', description: 'Restrict to one subscription.' },\n cursor: {\n type: 'number',\n description: 'Cursor from a prior toon_read.',\n },\n limit: {\n type: 'number',\n description: 'Max events to return (default 200).',\n },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'toon_open_channel',\n description:\n 'Open (or return the existing) payment channel for a destination. ' +\n 'Channels open lazily on first publish; use this to pre-open.',\n inputSchema: {\n type: 'object',\n properties: {\n destination: {\n type: 'string',\n description: 'ILP destination (default: apex).',\n },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'toon_channels',\n description:\n 'List tracked payment channels with their nonce watermark and cumulative ' +\n 'transferred amount.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_swap',\n description:\n 'Pay a mill peer (asset A) to receive asset B plus a signed target-chain ' +\n 'claim. Builds the NIP-59 gift-wrapped kind:20032 swap rumor and streams ' +\n 'it; the source-asset claim is signed against the open apex channel (the ' +\n 'mill must be routed via apexChildPeers). Returns the accumulated, ' +\n 'decrypted target-chain claim(s) and settlement metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n destination: {\n type: 'string',\n description: 'Mill peer ILP destination (e.g. g.townhouse.mill).',\n },\n amount: {\n type: 'string',\n description: 'Total source-asset amount to swap, source micro-units.',\n },\n millPubkey: {\n type: 'string',\n description:\n \"Mill's 64-char lowercase hex Nostr pubkey (gift-wrap recipient).\",\n },\n pair: {\n type: 'object',\n description:\n 'The swap pair (from kind:10032 discovery or operator-supplied): ' +\n '{ from:{assetCode,assetScale,chain}, to:{...}, rate, minAmount?, maxAmount? }.',\n },\n chainRecipient: {\n type: 'string',\n description:\n \"Sender's payout address on pair.to.chain (EVM 0x-hex / Solana / Mina base58).\",\n },\n packetCount: {\n type: 'number',\n description: 'Split the swap into N equal packets (default 1).',\n },\n },\n required: [\n 'destination',\n 'amount',\n 'millPubkey',\n 'pair',\n 'chainRecipient',\n ],\n additionalProperties: false,\n },\n },\n];\n\n/**\n * Dispatch an MCP tool call to the daemon control plane. Always resolves with a\n * `ToolResult` (errors are encoded as `isError: true` text, not thrown, so the\n * agent sees a readable message). A retryable error (daemon still\n * bootstrapping) yields a clear \"retry shortly\" message.\n */\nexport async function dispatchTool(\n client: ControlClient,\n name: string,\n args: Record<string, unknown>\n): Promise<ToolResult> {\n try {\n switch (name) {\n case 'toon_status':\n return ok(await client.status());\n case 'toon_identity': {\n const s = await client.status();\n return ok({\n identity: s.identity,\n ready: s.ready,\n bootstrapping: s.bootstrapping,\n });\n }\n case 'toon_publish':\n return ok(await client.publish(args as unknown as PublishRequest));\n case 'toon_subscribe':\n return ok(\n await client.subscribe({\n filters: args['filters'] as NostrFilter | NostrFilter[],\n ...(typeof args['subId'] === 'string'\n ? { subId: args['subId'] }\n : {}),\n })\n );\n case 'toon_read':\n return ok(\n await client.events({\n ...(typeof args['subId'] === 'string'\n ? { subId: args['subId'] }\n : {}),\n ...(typeof args['cursor'] === 'number'\n ? { cursor: args['cursor'] }\n : {}),\n ...(typeof args['limit'] === 'number'\n ? { limit: args['limit'] }\n : {}),\n })\n );\n case 'toon_open_channel':\n return ok(\n await client.openChannel(\n typeof args['destination'] === 'string'\n ? { destination: args['destination'] }\n : {}\n )\n );\n case 'toon_channels':\n return ok(await client.channels());\n case 'toon_swap':\n return ok(\n await client.swap({\n destination: String(args['destination']),\n amount: String(args['amount']),\n millPubkey: String(args['millPubkey']),\n pair: args['pair'] as SwapRequest['pair'],\n chainRecipient: String(args['chainRecipient']),\n ...(typeof args['packetCount'] === 'number'\n ? { packetCount: args['packetCount'] }\n : {}),\n })\n );\n default:\n return err(`Unknown tool: ${name}`);\n }\n } catch (e) {\n if (e instanceof ControlApiError && e.retryable) {\n return err(\n `TOON client is still bootstrapping (the anon proxy / BTP session can take ` +\n `30–90s) — retry shortly. (${e.message})`\n );\n }\n if (e instanceof DaemonUnreachableError) {\n return err(\n `TOON client daemon is not reachable at ${e.baseUrl}. It may have failed ` +\n `to start — check ~/.toon-client/daemon.log.`\n );\n }\n if (e instanceof ControlApiError) {\n return err(`${e.message}${e.detail ? `: ${e.detail}` : ''}`);\n }\n return err(e instanceof Error ? e.message : String(e));\n }\n}\n\nfunction ok(data: unknown): ToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };\n}\n\nfunction err(message: string): ToolResult {\n return { content: [{ type: 'text', text: message }], isError: true };\n}\n"],"mappings":"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","names":[]}
package/dist/daemon.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
3
3
  import {
4
4
  ClientRunner,
5
5
  registerRoutes
6
- } from "./chunk-2MY6AQK6.js";
6
+ } from "./chunk-4K6S3VPM.js";
7
7
  import {
8
8
  ControlClient,
9
9
  ToonClient,
@@ -16,12 +16,13 @@ import {
16
16
  resolveConfig,
17
17
  spawnDaemonDetached,
18
18
  waitForReady
19
- } from "./chunk-TEMKXTNH.js";
19
+ } from "./chunk-QHNSETFB.js";
20
20
  import "./chunk-32QD72IL.js";
21
21
  import "./chunk-QTDCFXPF.js";
22
22
  import "./chunk-LR7W2ISE.js";
23
23
  import "./chunk-VA7XC4FD.js";
24
24
  import "./chunk-SKQTKZIH.js";
25
+ import "./chunk-F22GNSF6.js";
25
26
 
26
27
  // src/daemon.ts
27
28
  import Fastify from "fastify";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/daemon.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-clientd` — the always-on, detached TOON client daemon. It owns the two\n * long-lived connections that cannot live in an ephemeral agent session:\n * • a BTP session to the apex/connector (paid writes), and\n * • a persistent town-relay Nostr-WS subscription (free reads),\n * plus the payment channels (with a persisted nonce watermark) and the signer.\n *\n * Subcommands:\n * toon-clientd run Run the daemon in the foreground (used internally by\n * the detached spawn; logs to stdout).\n * toon-clientd start Spawn the daemon detached and wait until reachable.\n * toon-clientd stop Stop a running daemon (SIGTERM the locked PID).\n * toon-clientd status Print the daemon status as JSON.\n */\n\nimport Fastify from 'fastify';\nimport { ToonClient } from '@toon-protocol/client';\nimport {\n defaultConfigPath,\n readConfigFile,\n resolveConfig,\n type ResolvedDaemonConfig,\n} from './daemon/config.js';\nimport { ClientRunner, type ToonClientLike } from './daemon/client-runner.js';\nimport { registerRoutes } from './daemon/routes.js';\nimport {\n acquireLock,\n isProcessAlive,\n readPid,\n releaseLock,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\nimport { ControlClient } from './control-client.js';\n\nfunction baseUrl(config: ResolvedDaemonConfig): string {\n return `http://127.0.0.1:${config.httpPort}`;\n}\n\nfunction loadResolvedConfig(): ResolvedDaemonConfig {\n const path = process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath();\n return resolveConfig(readConfigFile(path));\n}\n\n/** Run the daemon in the foreground (the detached child's actual work). */\nasync function runForeground(): Promise<void> {\n acquireLock();\n const config = loadResolvedConfig();\n const log = (msg: string): void => console.error(msg);\n\n const runner = new ClientRunner({\n config,\n createClient: () =>\n new ToonClient(config.toonClientConfig) as unknown as ToonClientLike,\n logger: log,\n });\n\n const app = Fastify({ logger: false });\n registerRoutes(app, runner);\n\n // Begin bootstrap (non-blocking) before listening so /status is immediately\n // reachable and reports `bootstrapping: true` while anon/BTP come up.\n runner.start();\n\n await app.listen({ host: '127.0.0.1', port: config.httpPort });\n log(`[toon-clientd] listening on ${baseUrl(config)}`);\n\n let shuttingDown = false;\n const shutdown = async (signal: string): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n log(`[toon-clientd] received ${signal}, shutting down`);\n try {\n await app.close();\n } catch {\n /* ignore */\n }\n await runner.stop();\n releaseLock();\n setTimeout(() => process.exit(0), 500).unref();\n };\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n process.on('SIGINT', () => void shutdown('SIGINT'));\n}\n\n/** Spawn the daemon detached and wait until the control plane responds. */\nasync function start(): Promise<void> {\n const config = loadResolvedConfig();\n const url = baseUrl(config);\n const existing = readPid();\n if (existing !== null && isProcessAlive(existing)) {\n console.log(`toon-clientd already running (pid ${existing}) at ${url}`);\n return;\n }\n const pid = spawnDaemonDetached();\n const ok = await waitForReady(url, 20_000);\n if (ok) {\n console.log(`toon-clientd started (pid ${pid}) at ${url}`);\n } else {\n console.error(\n `toon-clientd spawned (pid ${pid}) but did not become reachable at ${url} in time. ` +\n `Check the daemon log.`\n );\n process.exitCode = 1;\n }\n}\n\n/** Stop a running daemon via SIGTERM on the locked PID. */\nasync function stop(): Promise<void> {\n const pid = readPid();\n if (pid === null || !isProcessAlive(pid)) {\n console.log('toon-clientd is not running');\n releaseLock();\n return;\n }\n process.kill(pid, 'SIGTERM');\n // Wait briefly for the process to exit.\n for (let i = 0; i < 40; i++) {\n if (!isProcessAlive(pid)) break;\n await new Promise((r) => setTimeout(r, 100));\n }\n console.log(`toon-clientd stopped (pid ${pid})`);\n}\n\n/** Print the daemon status JSON. */\nasync function status(): Promise<void> {\n const config = loadResolvedConfig();\n const client = new ControlClient({ baseUrl: baseUrl(config) });\n try {\n const s = await client.status();\n console.log(JSON.stringify(s, null, 2));\n } catch {\n console.log(JSON.stringify({ running: false }, null, 2));\n process.exitCode = 1;\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2] ?? 'run';\n switch (cmd) {\n case 'run':\n await runForeground();\n break;\n case 'start':\n await start();\n break;\n case 'stop':\n await stop();\n break;\n case 'status':\n await status();\n break;\n default:\n console.error(\n `Unknown command \"${cmd}\". Usage: toon-clientd <run|start|stop|status>`\n );\n process.exitCode = 1;\n }\n}\n\nmain().catch((err) => {\n console.error(\n err instanceof Error ? (err.stack ?? err.message) : String(err)\n );\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/daemon.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-clientd` — the always-on, detached TOON client daemon. It owns the two\n * long-lived connections that cannot live in an ephemeral agent session:\n * • a BTP session to the apex/connector (paid writes), and\n * • a persistent town-relay Nostr-WS subscription (free reads),\n * plus the payment channels (with a persisted nonce watermark) and the signer.\n *\n * Subcommands:\n * toon-clientd run Run the daemon in the foreground (used internally by\n * the detached spawn; logs to stdout).\n * toon-clientd start Spawn the daemon detached and wait until reachable.\n * toon-clientd stop Stop a running daemon (SIGTERM the locked PID).\n * toon-clientd status Print the daemon status as JSON.\n */\n\nimport Fastify from 'fastify';\nimport { ToonClient } from '@toon-protocol/client';\nimport {\n defaultConfigPath,\n readConfigFile,\n resolveConfig,\n type ResolvedDaemonConfig,\n} from './daemon/config.js';\nimport { ClientRunner, type ToonClientLike } from './daemon/client-runner.js';\nimport { registerRoutes } from './daemon/routes.js';\nimport {\n acquireLock,\n isProcessAlive,\n readPid,\n releaseLock,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\nimport { ControlClient } from './control-client.js';\n\nfunction baseUrl(config: ResolvedDaemonConfig): string {\n return `http://127.0.0.1:${config.httpPort}`;\n}\n\nfunction loadResolvedConfig(): ResolvedDaemonConfig {\n const path = process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath();\n return resolveConfig(readConfigFile(path));\n}\n\n/** Run the daemon in the foreground (the detached child's actual work). */\nasync function runForeground(): Promise<void> {\n acquireLock();\n const config = loadResolvedConfig();\n const log = (msg: string): void => console.error(msg);\n\n const runner = new ClientRunner({\n config,\n createClient: () =>\n new ToonClient(config.toonClientConfig) as unknown as ToonClientLike,\n logger: log,\n });\n\n const app = Fastify({ logger: false });\n registerRoutes(app, runner);\n\n // Begin bootstrap (non-blocking) before listening so /status is immediately\n // reachable and reports `bootstrapping: true` while anon/BTP come up.\n runner.start();\n\n await app.listen({ host: '127.0.0.1', port: config.httpPort });\n log(`[toon-clientd] listening on ${baseUrl(config)}`);\n\n let shuttingDown = false;\n const shutdown = async (signal: string): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n log(`[toon-clientd] received ${signal}, shutting down`);\n try {\n await app.close();\n } catch {\n /* ignore */\n }\n await runner.stop();\n releaseLock();\n setTimeout(() => process.exit(0), 500).unref();\n };\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n process.on('SIGINT', () => void shutdown('SIGINT'));\n}\n\n/** Spawn the daemon detached and wait until the control plane responds. */\nasync function start(): Promise<void> {\n const config = loadResolvedConfig();\n const url = baseUrl(config);\n const existing = readPid();\n if (existing !== null && isProcessAlive(existing)) {\n console.log(`toon-clientd already running (pid ${existing}) at ${url}`);\n return;\n }\n const pid = spawnDaemonDetached();\n const ok = await waitForReady(url, 20_000);\n if (ok) {\n console.log(`toon-clientd started (pid ${pid}) at ${url}`);\n } else {\n console.error(\n `toon-clientd spawned (pid ${pid}) but did not become reachable at ${url} in time. ` +\n `Check the daemon log.`\n );\n process.exitCode = 1;\n }\n}\n\n/** Stop a running daemon via SIGTERM on the locked PID. */\nasync function stop(): Promise<void> {\n const pid = readPid();\n if (pid === null || !isProcessAlive(pid)) {\n console.log('toon-clientd is not running');\n releaseLock();\n return;\n }\n process.kill(pid, 'SIGTERM');\n // Wait briefly for the process to exit.\n for (let i = 0; i < 40; i++) {\n if (!isProcessAlive(pid)) break;\n await new Promise((r) => setTimeout(r, 100));\n }\n console.log(`toon-clientd stopped (pid ${pid})`);\n}\n\n/** Print the daemon status JSON. */\nasync function status(): Promise<void> {\n const config = loadResolvedConfig();\n const client = new ControlClient({ baseUrl: baseUrl(config) });\n try {\n const s = await client.status();\n console.log(JSON.stringify(s, null, 2));\n } catch {\n console.log(JSON.stringify({ running: false }, null, 2));\n process.exitCode = 1;\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2] ?? 'run';\n switch (cmd) {\n case 'run':\n await runForeground();\n break;\n case 'start':\n await start();\n break;\n case 'stop':\n await stop();\n break;\n case 'status':\n await status();\n break;\n default:\n console.error(\n `Unknown command \"${cmd}\". Usage: toon-clientd <run|start|stop|status>`\n );\n process.exitCode = 1;\n }\n}\n\nmain().catch((err) => {\n console.error(\n err instanceof Error ? (err.stack ?? err.message) : String(err)\n );\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
@@ -11,6 +11,7 @@ import {
11
11
  ristretto255_oprf,
12
12
  x25519
13
13
  } from "./chunk-QTDCFXPF.js";
14
+ import "./chunk-F22GNSF6.js";
14
15
  export {
15
16
  ED25519_TORSION_SUBGROUP,
16
17
  _map_to_curve_elligator2_curve25519,
@@ -23,4 +24,4 @@ export {
23
24
  ristretto255_oprf,
24
25
  x25519
25
26
  };
26
- //# sourceMappingURL=ed25519-OFFWPWRE.js.map
27
+ //# sourceMappingURL=ed25519-UB2JDJJK.js.map
@@ -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/gateway-QOK47RKS.js
4
5
  function rewriteUrlsForGateway(gatewayUrl, btpUrl, connectorUrl) {
@@ -12,4 +13,4 @@ function rewriteUrlsForGateway(gatewayUrl, btpUrl, connectorUrl) {
12
13
  export {
13
14
  rewriteUrlsForGateway
14
15
  };
15
- //# sourceMappingURL=gateway-QOK47RKS-HB65KIKC.js.map
16
+ //# sourceMappingURL=gateway-QOK47RKS-SEGTXBR3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../client/src/transport/gateway.ts"],"sourcesContent":["/**\n * Gateway transport for browser environments.\n *\n * Rewrites BTP and HTTP URLs to route through an ator gateway that handles\n * SOCKS5 proxying server-side. Browser clients connect to the gateway via\n * standard WebSocket/HTTP — no special transport code needed.\n */\n\n/**\n * Rewrites btpUrl and connectorUrl to route through a gateway.\n *\n * Gateway endpoint conventions:\n * - WebSocket: `ws(s)://<gateway>/btp` — proxies BTP connections\n * - HTTP: `http(s)://<gateway>/api` — proxies connector admin API\n *\n * @param gatewayUrl - Base URL of the ator gateway (http:// or https://)\n * @param btpUrl - Original BTP WebSocket URL (optional)\n * @param connectorUrl - Original connector HTTP URL (optional)\n */\nexport function rewriteUrlsForGateway(\n gatewayUrl: string,\n btpUrl?: string,\n connectorUrl?: string\n): { btpUrl?: string; connectorUrl?: string } {\n const base = gatewayUrl.replace(/\\/$/, '');\n\n // Derive WebSocket scheme from HTTP scheme\n const wsBase = base.replace(/^https:/, 'wss:').replace(/^http:/, 'ws:');\n\n return {\n btpUrl: btpUrl ? `${wsBase}/btp` : undefined,\n connectorUrl: connectorUrl ? `${base}/api` : undefined,\n };\n}\n"],"mappings":";;;AAmBO,SAAS,sBACd,YACA,QACA,cAC4C;AAC5C,QAAM,OAAO,WAAW,QAAQ,OAAO,EAAE;AAGzC,QAAM,SAAS,KAAK,QAAQ,WAAW,MAAM,EAAE,QAAQ,UAAU,KAAK;AAEtE,SAAO;IACL,QAAQ,SAAS,GAAG,MAAM,SAAS;IACnC,cAAc,eAAe,GAAG,IAAI,SAAS;EAC/C;AACF;","names":[]}
1
+ {"version":3,"sources":["../../client/src/transport/gateway.ts"],"sourcesContent":["/**\n * Gateway transport for browser environments.\n *\n * Rewrites BTP and HTTP URLs to route through an ator gateway that handles\n * SOCKS5 proxying server-side. Browser clients connect to the gateway via\n * standard WebSocket/HTTP — no special transport code needed.\n */\n\n/**\n * Rewrites btpUrl and connectorUrl to route through a gateway.\n *\n * Gateway endpoint conventions:\n * - WebSocket: `ws(s)://<gateway>/btp` — proxies BTP connections\n * - HTTP: `http(s)://<gateway>/api` — proxies connector admin API\n *\n * @param gatewayUrl - Base URL of the ator gateway (http:// or https://)\n * @param btpUrl - Original BTP WebSocket URL (optional)\n * @param connectorUrl - Original connector HTTP URL (optional)\n */\nexport function rewriteUrlsForGateway(\n gatewayUrl: string,\n btpUrl?: string,\n connectorUrl?: string\n): { btpUrl?: string; connectorUrl?: string } {\n const base = gatewayUrl.replace(/\\/$/, '');\n\n // Derive WebSocket scheme from HTTP scheme\n const wsBase = base.replace(/^https:/, 'wss:').replace(/^http:/, 'ws:');\n\n return {\n btpUrl: btpUrl ? `${wsBase}/btp` : undefined,\n connectorUrl: connectorUrl ? `${base}/api` : undefined,\n };\n}\n"],"mappings":";;;;AAmBO,SAAS,sBACd,YACA,QACA,cAC4C;AAC5C,QAAM,OAAO,WAAW,QAAQ,OAAO,EAAE;AAGzC,QAAM,SAAS,KAAK,QAAQ,WAAW,MAAM,EAAE,QAAQ,UAAU,KAAK;AAEtE,SAAO;IACL,QAAQ,SAAS,GAAG,MAAM,SAAS;IACnC,cAAc,eAAe,GAAG,IAAI,SAAS;EAC/C;AACF;","names":[]}
@@ -4,8 +4,9 @@ import {
4
4
  hmac
5
5
  } from "./chunk-32QD72IL.js";
6
6
  import "./chunk-VA7XC4FD.js";
7
+ import "./chunk-F22GNSF6.js";
7
8
  export {
8
9
  HMAC,
9
10
  hmac
10
11
  };
11
- //# sourceMappingURL=hmac-7WSXTWW4.js.map
12
+ //# sourceMappingURL=hmac-26UC6YKX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
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-4K6S3VPM.js";
9
9
  import {
10
10
  TOOL_DEFINITIONS,
11
11
  dispatchTool
12
- } from "./chunk-CH37M2I2.js";
12
+ } from "./chunk-U227QSQG.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-QHNSETFB.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-U227QSQG.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-QHNSETFB.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.0",
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,7 +45,8 @@
45
45
  "typescript": "^5.3.0",
46
46
  "vitest": "^1.0.0",
47
47
  "@toon-protocol/client": "0.9.1",
48
- "@toon-protocol/core": "1.4.1"
48
+ "@toon-protocol/core": "1.4.1",
49
+ "@toon-protocol/sdk": "0.5.0"
49
50
  },
50
51
  "engines": {
51
52
  "node": ">=20"