@toon-protocol/client-mcp 0.26.2
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.
- package/LICENSE +190 -0
- package/README.md +261 -0
- package/dist/anon-proxy-6N362VEV-M7AX2QD7.js +24 -0
- package/dist/anon-proxy-6N362VEV-M7AX2QD7.js.map +1 -0
- package/dist/chunk-245J23EB.js +278 -0
- package/dist/chunk-245J23EB.js.map +1 -0
- package/dist/chunk-2SGZPDGE.js +625 -0
- package/dist/chunk-2SGZPDGE.js.map +1 -0
- package/dist/chunk-32QD72IL.js +83 -0
- package/dist/chunk-32QD72IL.js.map +1 -0
- package/dist/chunk-5YIZ2JQO.js +205 -0
- package/dist/chunk-5YIZ2JQO.js.map +1 -0
- package/dist/chunk-LR7W2ISE.js +657 -0
- package/dist/chunk-LR7W2ISE.js.map +1 -0
- package/dist/chunk-QTDCFXPF.js +2802 -0
- package/dist/chunk-QTDCFXPF.js.map +1 -0
- package/dist/chunk-VA7XC4FD.js +185 -0
- package/dist/chunk-VA7XC4FD.js.map +1 -0
- package/dist/chunk-WMYY5I3H.js +10818 -0
- package/dist/chunk-WMYY5I3H.js.map +1 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +137 -0
- package/dist/daemon.js.map +1 -0
- package/dist/ed25519-OFFWPWRE.js +26 -0
- package/dist/ed25519-OFFWPWRE.js.map +1 -0
- package/dist/gateway-QOK47RKS-HB65KIKC.js +15 -0
- package/dist/gateway-QOK47RKS-HB65KIKC.js.map +1 -0
- package/dist/hmac-7WSXTWW4.js +11 -0
- package/dist/hmac-7WSXTWW4.js.map +1 -0
- package/dist/index.d.ts +642 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +80 -0
- package/dist/mcp.js.map +1 -0
- package/dist/sha512-LMOIUNFJ.js +33 -0
- package/dist/sha512-LMOIUNFJ.js.map +1 -0
- package/dist/socks5-WTJBYGME-IXWLQDE7.js +138 -0
- package/dist/socks5-WTJBYGME-IXWLQDE7.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/relay-subscription.ts","../src/daemon/apex-channel-store.ts","../src/daemon/client-runner.ts","../src/daemon/routes.ts"],"sourcesContent":["/**\n * Persistent town-relay Nostr-WS subscription — the read half of the TOON\n * client, which `@toon-protocol/client` does not provide (its bootstrap only\n * issues one-shot WS queries and `DiscoveryTracker` is passive).\n *\n * Reads are FREE: this opens a long-lived NIP-01 connection to the town relay,\n * keeps a bounded ring buffer of received events (de-duplicated by `event.id`),\n * and lets callers drain new events via a monotonic cursor. It auto-reconnects\n * with exponential backoff and re-issues every active REQ on reconnect.\n *\n * The WebSocket is injectable (`wsFactory`) so unit tests can drive the wire\n * protocol without a real relay; the default factory uses the `ws` package and,\n * when a `socks5h://` proxy is configured, routes through it so `.anyone`\n * hidden-service relays are reachable.\n */\n\nimport { createRequire } from 'node:module';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { NostrFilter } from './control-api.js';\n\n// ESM has no `require`; synchronously load the node-only `ws`/`socks-proxy-agent`\n// deps for the default WebSocket factory without forcing every caller (e.g. the\n// browser-style injected-factory path in tests) to pull them in.\nconst nodeRequire = createRequire(import.meta.url);\n\n/** Minimal WebSocket surface this module depends on (subset of `ws`). */\nexport interface MinimalWebSocket {\n send(data: string): void;\n close(): void;\n on(event: 'open' | 'close', cb: () => void): void;\n on(event: 'message', cb: (data: unknown) => void): void;\n on(event: 'error', cb: (err: unknown) => void): void;\n}\n\nexport type WebSocketFactory = (url: string) => MinimalWebSocket;\n\nexport interface RelaySubscriptionOptions {\n /** Town relay WS URL, e.g. `wss://<host>.anyone/` or `ws://localhost:7100`. */\n relayUrl: string;\n /** Optional `socks5h://host:port` proxy for `.anyone` relays. */\n socksProxy?: string;\n /** Max events retained in the ring buffer (oldest evicted). Default 5000. */\n bufferSize?: number;\n /** Base reconnect delay, ms. Default 1000. */\n reconnectBaseMs?: number;\n /** Max reconnect delay, ms. Default 30000. */\n reconnectMaxMs?: number;\n /** Inject a WebSocket factory (tests / proxy customisation). */\n wsFactory?: WebSocketFactory;\n /**\n * Decode an `EVENT` payload that arrived as a string. The TOON relay sends\n * events TOON-encoded (key/value text) rather than as a JSON object, so the\n * daemon injects a TOON decoder here. When the payload is already a JSON\n * object it is used directly and this is not called.\n */\n decodeEvent?: (raw: string) => NostrEvent;\n /** Optional logger. */\n logger?: (msg: string) => void;\n}\n\ninterface BufferedEvent {\n seq: number;\n subId: string;\n event: NostrEvent;\n}\n\n/** Result of {@link RelaySubscription.getEvents}. */\nexport interface DrainResult {\n events: NostrEvent[];\n cursor: number;\n hasMore: boolean;\n}\n\nconst DEFAULT_BUFFER = 5000;\nconst DEFAULT_BASE_MS = 1000;\nconst DEFAULT_MAX_MS = 30_000;\n\n/** Shared no-op used as the default logger. */\nconst noop = (): void => undefined;\n\nexport class RelaySubscription {\n private readonly relayUrl: string;\n private readonly socksProxy?: string;\n private readonly bufferSize: number;\n private readonly reconnectBaseMs: number;\n private readonly reconnectMaxMs: number;\n private readonly log: (msg: string) => void;\n private readonly wsFactory: WebSocketFactory;\n private readonly decodeEvent?: (raw: string) => NostrEvent;\n\n /** Active subscriptions: subId -> filters (re-sent on every (re)connect). */\n private readonly subscriptions = new Map<string, NostrFilter[]>();\n\n /** Ring buffer of received events, ordered by ascending `seq`. */\n private buffer: BufferedEvent[] = [];\n /** De-dup index: event.id -> seq (kept in lockstep with the buffer). */\n private readonly seen = new Set<string>();\n private seqCounter = 0;\n private subIdCounter = 0;\n\n private ws: MinimalWebSocket | null = null;\n private connected = false;\n private closing = false;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(opts: RelaySubscriptionOptions) {\n this.relayUrl = opts.relayUrl;\n this.socksProxy = opts.socksProxy;\n this.bufferSize = opts.bufferSize ?? DEFAULT_BUFFER;\n this.reconnectBaseMs = opts.reconnectBaseMs ?? DEFAULT_BASE_MS;\n this.reconnectMaxMs = opts.reconnectMaxMs ?? DEFAULT_MAX_MS;\n this.log = opts.logger ?? noop;\n this.wsFactory = opts.wsFactory ?? defaultWebSocketFactory(this.socksProxy);\n this.decodeEvent = opts.decodeEvent;\n }\n\n /** Whether the underlying socket is currently open. */\n isConnected(): boolean {\n return this.connected;\n }\n\n /** Number of events currently held in the buffer. */\n bufferedCount(): number {\n return this.buffer.length;\n }\n\n /** Active subscription ids. */\n activeSubscriptions(): string[] {\n return [...this.subscriptions.keys()];\n }\n\n /** Open the connection (idempotent). */\n start(): void {\n this.closing = false;\n if (this.ws) return;\n this.open();\n }\n\n /**\n * Register a persistent subscription and (if connected) send the REQ.\n * Returns the subscription id (caller-supplied or generated).\n */\n subscribe(filters: NostrFilter | NostrFilter[], subId?: string): string {\n const id = subId ?? `sub-${++this.subIdCounter}`;\n const list = Array.isArray(filters) ? filters : [filters];\n this.subscriptions.set(id, list);\n if (this.connected) this.sendReq(id, list);\n return id;\n }\n\n /** Cancel a subscription and send CLOSE if connected. */\n unsubscribe(subId: string): void {\n if (!this.subscriptions.delete(subId)) return;\n if (this.connected) this.sendRaw(['CLOSE', subId]);\n }\n\n /**\n * Drain events newer than `cursor`. The cursor is the highest `seq` returned;\n * pass it back to fetch only events received since. Filtering by `subId`\n * restricts to one subscription.\n */\n getEvents(\n opts: { subId?: string; cursor?: number; limit?: number } = {}\n ): DrainResult {\n const after = opts.cursor ?? 0;\n const limit = opts.limit ?? 200;\n const matches = this.buffer.filter(\n (b) =>\n b.seq > after && (opts.subId === undefined || b.subId === opts.subId)\n );\n const page = matches.slice(0, limit);\n const hasMore = matches.length > page.length;\n const last = page.at(-1);\n const cursor = last ? last.seq : after;\n return { events: page.map((b) => b.event), cursor, hasMore };\n }\n\n /** Close the connection permanently and stop reconnecting. */\n close(): void {\n this.closing = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n /* ignore */\n }\n this.ws = null;\n }\n this.connected = false;\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private open(): void {\n let ws: MinimalWebSocket;\n try {\n ws = this.wsFactory(this.relayUrl);\n } catch (err) {\n this.log(`[relay] connect failed: ${errMsg(err)}`);\n this.scheduleReconnect();\n return;\n }\n this.ws = ws;\n\n ws.on('open', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.log(`[relay] connected to ${this.relayUrl}`);\n // Re-issue every active subscription.\n for (const [id, filters] of this.subscriptions) this.sendReq(id, filters);\n });\n\n ws.on('message', (data: unknown) => this.handleMessage(data));\n\n ws.on('error', (err: unknown) => {\n this.log(`[relay] socket error: ${errMsg(err)}`);\n });\n\n ws.on('close', () => {\n this.connected = false;\n this.ws = null;\n if (!this.closing) {\n this.log('[relay] disconnected; scheduling reconnect');\n this.scheduleReconnect();\n }\n });\n }\n\n private scheduleReconnect(): void {\n if (this.closing || this.reconnectTimer) return;\n const delay = Math.min(\n this.reconnectMaxMs,\n this.reconnectBaseMs * 2 ** this.reconnectAttempts\n );\n this.reconnectAttempts += 1;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n if (!this.closing) this.open();\n }, delay);\n // Don't keep the event loop alive solely for a reconnect timer.\n (this.reconnectTimer as { unref?: () => void }).unref?.();\n }\n\n private sendReq(subId: string, filters: NostrFilter[]): void {\n this.sendRaw(['REQ', subId, ...filters]);\n }\n\n private sendRaw(message: unknown[]): void {\n if (!this.ws || !this.connected) return;\n try {\n this.ws.send(JSON.stringify(message));\n } catch (err) {\n this.log(`[relay] send failed: ${errMsg(err)}`);\n }\n }\n\n private handleMessage(data: unknown): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(toText(data));\n } catch {\n return;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return;\n const type = parsed[0];\n switch (type) {\n case 'EVENT': {\n const subId = typeof parsed[1] === 'string' ? parsed[1] : '';\n const event = this.parseEventPayload(parsed[2]);\n if (event && typeof event.id === 'string')\n this.bufferEvent(subId, event);\n break;\n }\n case 'EOSE':\n // End of stored events — nothing to do; we keep streaming live events.\n break;\n case 'CLOSED':\n this.log(\n `[relay] subscription closed by relay: ${String(parsed[2] ?? '')}`\n );\n break;\n case 'NOTICE':\n this.log(`[relay] NOTICE: ${String(parsed[1] ?? '')}`);\n break;\n default:\n break;\n }\n }\n\n /**\n * Normalise an `EVENT` payload to a NostrEvent. Standard relays send a JSON\n * object; the TOON relay sends a TOON-encoded string, decoded via the injected\n * `decodeEvent`. Returns undefined when it can't be parsed.\n */\n private parseEventPayload(raw: unknown): NostrEvent | undefined {\n if (\n raw &&\n typeof raw === 'object' &&\n typeof (raw as NostrEvent).id === 'string'\n ) {\n return raw as NostrEvent;\n }\n if (typeof raw === 'string' && this.decodeEvent) {\n try {\n return this.decodeEvent(raw);\n } catch (err) {\n this.log(`[relay] event decode failed: ${errMsg(err)}`);\n return undefined;\n }\n }\n return undefined;\n }\n\n private bufferEvent(subId: string, event: NostrEvent): void {\n if (this.seen.has(event.id)) return; // de-dup by event.id\n this.seen.add(event.id);\n this.buffer.push({ seq: ++this.seqCounter, subId, event });\n if (this.buffer.length > this.bufferSize) {\n const evicted = this.buffer.shift();\n if (evicted) this.seen.delete(evicted.event.id);\n }\n }\n}\n\nfunction toText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (data instanceof Uint8Array) return Buffer.from(data).toString('utf8');\n if (Buffer.isBuffer(data)) return data.toString('utf8');\n return String(data);\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Default factory backed by the `ws` package. When a `socks5h://` proxy is\n * provided, routes the WebSocket through it via `socks-proxy-agent` (optional\n * dependency, loaded lazily so the bundle works without it).\n */\nfunction defaultWebSocketFactory(socksProxy?: string): WebSocketFactory {\n return (url: string): MinimalWebSocket => {\n const WebSocketImpl = nodeRequire('ws') as new (\n address: string,\n opts?: { agent?: unknown }\n ) => MinimalWebSocket;\n let agent: unknown;\n if (socksProxy) {\n const { SocksProxyAgent } = nodeRequire('socks-proxy-agent') as {\n SocksProxyAgent: new (proxy: string) => unknown;\n };\n agent = new SocksProxyAgent(socksProxy);\n }\n return new WebSocketImpl(url, agent ? { agent } : undefined);\n };\n}\n","/**\n * Persists the apex payment-channel id (+ its chain context) per\n * (destination, chain) so a daemon RESTART can resume the EXISTING on-chain\n * channel instead of opening a new one.\n *\n * Why this is needed: `ChannelManager` persists the off-chain nonce/cumulative\n * watermark (keyed by channelId) but NOT the peer→channelId mapping. So after a\n * restart `openChannel()` would open + re-deposit into a fresh channel, which\n * reverts on a chain where the deposit already exists. With the channelId saved\n * here, the runner instead calls `trackChannel(channelId, context)` — which\n * rehydrates the nonce from the channel store — and signs against the live\n * channel with zero on-chain writes.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/** Chain context needed to re-track a channel (matches ChannelManager.trackChannel). */\nexport interface PersistedChannelContext {\n chainType: string;\n chainId: number;\n tokenNetworkAddress: string;\n tokenAddress?: string;\n recipient?: string;\n}\n\nexport interface PersistedApexChannel {\n channelId: string;\n context: PersistedChannelContext;\n}\n\ntype Store = Record<string, PersistedApexChannel>;\n\nfunction key(destination: string, chain: string): string {\n return `${destination}|${chain}`;\n}\n\nfunction readStore(path: string): Store {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Store;\n } catch {\n return {};\n }\n}\n\n/** Load the saved apex channel for (destination, chain), or null. */\nexport function loadApexChannel(\n path: string,\n destination: string,\n chain: string\n): PersistedApexChannel | null {\n return readStore(path)[key(destination, chain)] ?? null;\n}\n\n/** Save the apex channel for (destination, chain) with mode 0o600. */\nexport function saveApexChannel(\n path: string,\n destination: string,\n chain: string,\n record: PersistedApexChannel\n): void {\n const store = readStore(path);\n store[key(destination, chain)] = record;\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(store, null, 2), { mode: 0o600 });\n}\n","/**\n * ClientRunner — the daemon's connection owner. Wraps a single `ToonClient`\n * (BTP session + payment channels + signer) plus a persistent\n * `RelaySubscription` (free reads), and exposes the high-level operations the\n * HTTP routes map onto.\n *\n * Bootstrap is asynchronous and non-blocking: `start()` returns immediately and\n * the connection comes up in the background (the managed anon proxy alone can\n * take 30–90s). Until it is ready, write operations report `bootstrapping` so\n * tools surface \"retry\" rather than hang.\n *\n * The `ToonClient` is injected via `ToonClientLike` so unit tests can drive the\n * runner with a fake — no BTP/anon/on-chain dependency.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { decodeEventFromToon } from '@toon-protocol/core';\nimport { RelaySubscription } from '../relay-subscription.js';\nimport type {\n ChannelsResponse,\n ChainStatus,\n EventsResponse,\n PublishResponse,\n StatusResponse,\n SubscribeRequest,\n SubscribeResponse,\n SwapResponse,\n} from '../control-api.js';\nimport type {\n EventsQuery,\n PublishRequest,\n SwapRequest,\n} from '../control-api.js';\nimport type { ApexNegotiationConfig, ResolvedDaemonConfig } from './config.js';\nimport {\n loadApexChannel,\n saveApexChannel,\n type PersistedChannelContext,\n} from './apex-channel-store.js';\n\n/** The subset of `ToonClient` the runner depends on. */\nexport interface ToonClientLike {\n start(): Promise<{ peersDiscovered: number; mode: string }>;\n stop(): Promise<void>;\n getPublicKey(): string;\n getEvmAddress(): string | undefined;\n getSolanaAddress(): string | undefined;\n getMinaAddress(): string | undefined;\n getNetworkStatus(): { evm: string; solana: string; mina: string } | undefined;\n publishEvent(\n event: NostrEvent,\n options?: { destination?: string; claim?: unknown; ilpAmount?: bigint }\n ): Promise<{\n success: boolean;\n eventId?: string;\n data?: string;\n error?: string;\n }>;\n signBalanceProof(channelId: string, amount: bigint): Promise<unknown>;\n openChannel(destination?: string): Promise<string>;\n getTrackedChannels(): string[];\n getChannelNonce(channelId: string): number;\n getChannelCumulativeAmount(channelId: string): bigint;\n sendSwapPacket(params: {\n destination: string;\n amount: bigint;\n toonData: Uint8Array;\n claim?: unknown;\n }): Promise<{\n accepted: boolean;\n data?: string;\n code?: string;\n message?: string;\n }>;\n}\n\nexport interface ClientRunnerDeps {\n config: ResolvedDaemonConfig;\n /** Factory producing the (real or fake) ToonClient. */\n createClient: () => ToonClientLike;\n /** Factory producing the relay subscription (defaults to the real one). */\n createRelay?: () => RelaySubscription;\n logger?: (msg: string) => void;\n}\n\nexport class ClientRunner {\n private readonly config: ResolvedDaemonConfig;\n private readonly client: ToonClientLike;\n private readonly relay: RelaySubscription;\n private readonly log: (msg: string) => void;\n\n private readonly startedAt = Date.now();\n private bootstrapping = false;\n private ready = false;\n private lastError: string | undefined;\n /** Channel opened against the default apex destination. */\n private apexChannelId: string | undefined;\n private stopped = false;\n\n constructor(deps: ClientRunnerDeps) {\n this.config = deps.config;\n this.client = deps.createClient();\n this.relay =\n deps.createRelay?.() ??\n new RelaySubscription({\n relayUrl: deps.config.relayUrl,\n socksProxy: deps.config.socksProxy,\n logger: deps.logger,\n // The TOON relay sends events TOON-encoded (text) on reads, not as JSON.\n decodeEvent: (raw) =>\n decodeEventFromToon(new TextEncoder().encode(raw)),\n });\n this.log = deps.logger ?? ((): void => undefined);\n }\n\n /**\n * Begin bootstrapping in the background. Resolves once kicked off; the\n * connection becomes ready asynchronously. Awaitable for tests via the\n * returned promise of the underlying work.\n */\n start(): void {\n if (this.bootstrapping || this.ready) return;\n this.bootstrapping = true;\n // Reads can start immediately and independently of the paid-write path.\n this.relay.start();\n void this.bootstrap();\n }\n\n /** The background bootstrap routine (exposed for awaiting in tests). */\n async bootstrap(): Promise<void> {\n try {\n await this.client.start();\n this.injectApexNegotiation(this.config.apex);\n this.apexChannelId = await this.openOrResumeApexChannel();\n this.ready = true;\n this.lastError = undefined;\n this.log(`[runner] ready; apex channel ${this.apexChannelId}`);\n } catch (err) {\n this.lastError = err instanceof Error ? err.message : String(err);\n this.log(`[runner] bootstrap failed: ${this.lastError}`);\n } finally {\n this.bootstrapping = false;\n }\n }\n\n /**\n * Open the apex channel — or, on a restart, RESUME the existing one.\n *\n * `ChannelManager` persists the nonce watermark (by channelId) but not the\n * peer→channelId mapping, so a naive `openChannel()` after restart re-deposits\n * into a fresh channel and reverts on-chain. We persist the channelId here and,\n * when present, `trackChannel()` the live channel (which rehydrates the nonce\n * from the channel store) — no on-chain write, watermark continues.\n */\n private async openOrResumeApexChannel(): Promise<string> {\n const { destination, chain, apexChannelStorePath } = this.config;\n const saved = loadApexChannel(apexChannelStorePath, destination, chain);\n const cm = (\n this.client as unknown as {\n channelManager?: {\n trackChannel?: (id: string, ctx: PersistedChannelContext) => void;\n };\n }\n ).channelManager;\n\n if (saved && cm && typeof cm.trackChannel === 'function') {\n cm.trackChannel(saved.channelId, saved.context);\n this.log(\n `[runner] resumed apex channel ${saved.channelId} (tracked, no re-deposit)`\n );\n return saved.channelId;\n }\n\n // First open for this (destination, chain): open on-chain and persist the\n // channelId + context so the next restart resumes instead of re-depositing.\n const channelId = await this.client.openChannel(destination);\n if (this.config.apex) {\n const a = this.config.apex;\n saveApexChannel(apexChannelStorePath, destination, chain, {\n channelId,\n context: {\n chainType: a.chain,\n chainId: a.chainId,\n tokenNetworkAddress: a.tokenNetwork ?? '',\n tokenAddress: a.tokenAddress,\n recipient: a.settlementAddress,\n },\n });\n }\n return channelId;\n }\n\n /**\n * Inject the apex settlement negotiation directly into the ToonClient when\n * configured. Required for HS / direct-apex mode where bootstrap discovers 0\n * peers (no relay-based discovery). Mirrors the docker entrypoint's approach.\n */\n private injectApexNegotiation(apex: ApexNegotiationConfig | undefined): void {\n if (!apex) return;\n const negotiations = (\n this.client as unknown as {\n peerNegotiations?: Map<string, unknown>;\n }\n ).peerNegotiations;\n if (!(negotiations instanceof Map)) {\n throw new Error(\n 'ToonClient.peerNegotiations layout changed — cannot inject apex negotiation'\n );\n }\n negotiations.set(apex.peerId, {\n chain: apex.chainKey,\n chainType: apex.chain,\n chainId: apex.chainId,\n settlementAddress: apex.settlementAddress,\n tokenAddress: apex.tokenAddress,\n tokenNetwork: apex.tokenNetwork,\n });\n this.log(`[runner] injected apex negotiation for peer \"${apex.peerId}\"`);\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n isBootstrapping(): boolean {\n return this.bootstrapping;\n }\n\n getStatus(): StatusResponse {\n const net = this.client.getNetworkStatus();\n const network: ChainStatus[] | undefined = net\n ? (['evm', 'solana', 'mina'] as const).map((c) => ({\n chain: c,\n ready: net[c] === 'configured',\n detail: net[c],\n }))\n : undefined;\n return {\n uptimeMs: Date.now() - this.startedAt,\n bootstrapping: this.bootstrapping,\n ready: this.ready,\n settlementChain: this.config.chain,\n identity: {\n nostrPubkey: safe(() => this.client.getPublicKey()) ?? '',\n evmAddress: safe(() => this.client.getEvmAddress()),\n solanaAddress: safe(() => this.client.getSolanaAddress()),\n minaAddress: safe(() => this.client.getMinaAddress()),\n },\n transport: {\n type: this.config.socksProxy ? 'socks5' : 'direct',\n socksProxy: this.config.socksProxy,\n btpUrl: this.config.toonClientConfig.btpUrl,\n },\n relay: {\n url: this.config.relayUrl,\n connected: this.relay.isConnected(),\n buffered: this.relay.bufferedCount(),\n subscriptions: this.relay.activeSubscriptions(),\n },\n ...(network ? { network } : {}),\n ...(this.lastError ? { lastError: this.lastError } : {}),\n };\n }\n\n /** Pay-to-write a single event. Throws NOT_READY while bootstrapping. */\n async publish(req: PublishRequest): Promise<PublishResponse> {\n this.assertReady();\n const channelId =\n this.apexChannelId ?? (await this.client.openChannel(req.destination));\n const fee =\n req.fee !== undefined ? BigInt(req.fee) : this.config.feePerEvent;\n const claim = await this.client.signBalanceProof(channelId, fee);\n const result = await this.client.publishEvent(req.event, {\n destination: req.destination,\n claim,\n ilpAmount: fee,\n });\n if (!result.success) {\n throw new PublishRejectedError(result.error ?? 'relay rejected event');\n }\n return {\n eventId: result.eventId ?? req.event.id,\n data: result.data,\n channelId,\n nonce: this.client.getChannelNonce(channelId),\n };\n }\n\n /** Register a free-read subscription (does not require the paid path). */\n subscribe(req: SubscribeRequest): SubscribeResponse {\n const subId = this.relay.subscribe(req.filters, req.subId);\n return { subId };\n }\n\n /** Drain buffered events newer than the cursor (free read). */\n getEvents(query: EventsQuery): EventsResponse {\n const opts: { subId?: string; cursor?: number; limit?: number } = {};\n if (query.subId !== undefined) opts.subId = query.subId;\n if (query.cursor !== undefined) opts.cursor = query.cursor;\n if (query.limit !== undefined) opts.limit = query.limit;\n const { events, cursor, hasMore } = this.relay.getEvents(opts);\n return { events, cursor, hasMore };\n }\n\n /** Open (or return) a payment channel for a destination. */\n async openChannel(destination?: string): Promise<{ channelId: string }> {\n this.assertReady();\n const channelId = await this.client.openChannel(\n destination ?? this.config.destination\n );\n if (!destination || destination === this.config.destination) {\n this.apexChannelId = channelId;\n }\n return { channelId };\n }\n\n /** List tracked channels with their nonce watermark + cumulative amount. */\n getChannels(): ChannelsResponse {\n const channels = this.client.getTrackedChannels().map((channelId) => ({\n channelId,\n nonce: this.client.getChannelNonce(channelId),\n cumulativeAmount: this.client\n .getChannelCumulativeAmount(channelId)\n .toString(),\n }));\n return { channels };\n }\n\n /** Send a swap packet to a mill peer. */\n async swap(req: SwapRequest): Promise<SwapResponse> {\n this.assertReady();\n const toonData = req.toonData\n ? new Uint8Array(Buffer.from(req.toonData, 'base64'))\n : new Uint8Array(0);\n const result = await this.client.sendSwapPacket({\n destination: req.destination,\n amount: BigInt(req.amount),\n toonData,\n });\n return {\n accepted: result.accepted,\n data: result.data,\n code: result.code,\n message: result.message,\n };\n }\n\n /** Graceful teardown of the relay subscription + ToonClient. */\n async stop(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n this.relay.close();\n try {\n await this.client.stop();\n } catch (err) {\n this.log(\n `[runner] client stop error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n private assertReady(): void {\n if (!this.ready) {\n throw new NotReadyError(\n this.bootstrapping\n ? 'Daemon is still bootstrapping (BTP/anon coming up) — retry shortly.'\n : (this.lastError ?? 'Daemon is not ready.')\n );\n }\n }\n}\n\n/** Thrown by paid-write operations while the daemon is not yet ready. */\nexport class NotReadyError extends Error {\n readonly retryable = true;\n constructor(message: string) {\n super(message);\n this.name = 'NotReadyError';\n }\n}\n\n/** Thrown when the relay/connector rejects a paid write. */\nexport class PublishRejectedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PublishRejectedError';\n }\n}\n\nfunction safe<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n","/**\n * Fastify route registration for the `toon-clientd` control plane. Each route\n * is a thin adapter: parse/validate the request, call the `ClientRunner`, map\n * errors to the uniform `ErrorResponse` envelope.\n *\n * Bound to loopback only by the daemon entry — there is no auth layer because\n * the surface never leaves `127.0.0.1`.\n */\n\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { ClientRunner } from './client-runner.js';\nimport { NotReadyError, PublishRejectedError } from './client-runner.js';\nimport type {\n EventsQuery,\n OpenChannelRequest,\n PublishRequest,\n SubscribeRequest,\n SwapRequest,\n} from '../control-api.js';\n\nexport function registerRoutes(\n app: FastifyInstance,\n runner: ClientRunner\n): void {\n app.get('/status', async () => runner.getStatus());\n\n app.post<{ Body: PublishRequest }>('/publish', async (req, reply) => {\n const body = req.body;\n if (!body || !isSignedEvent(body.event)) {\n return sendError(reply, 400, 'invalid_event', {\n detail: 'body.event must be a fully-signed Nostr event (id + sig).',\n });\n }\n try {\n return await runner.publish(body);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n\n app.post<{ Body: SubscribeRequest }>('/subscribe', async (req, reply) => {\n const body = req.body;\n if (!body || body.filters === undefined) {\n return sendError(reply, 400, 'invalid_filters', {\n detail:\n 'body.filters is required (a NIP-01 filter or array of filters).',\n });\n }\n return runner.subscribe(body);\n });\n\n app.get<{ Querystring: { subId?: string; cursor?: string; limit?: string } }>(\n '/events',\n async (req) => {\n const q = req.query;\n const query: EventsQuery = {};\n if (q.subId) query.subId = q.subId;\n if (q.cursor !== undefined) query.cursor = Number(q.cursor);\n if (q.limit !== undefined) query.limit = Number(q.limit);\n return runner.getEvents(query);\n }\n );\n\n app.post<{ Body: OpenChannelRequest }>('/channels', async (req, reply) => {\n try {\n return await runner.openChannel(req.body?.destination);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n\n app.get('/channels', async () => runner.getChannels());\n\n app.post<{ Body: SwapRequest }>('/swap', async (req, reply) => {\n const body = req.body;\n if (!body || !body.destination || body.amount === undefined) {\n return sendError(reply, 400, 'invalid_swap', {\n detail: 'body.destination and body.amount are required.',\n });\n }\n try {\n return await runner.swap(body);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n}\n\nfunction isSignedEvent(event: unknown): event is NostrEvent {\n if (typeof event !== 'object' || event === null) return false;\n const e = event as Record<string, unknown>;\n return (\n typeof e['id'] === 'string' &&\n typeof e['sig'] === 'string' &&\n typeof e['pubkey'] === 'string' &&\n typeof e['kind'] === 'number'\n );\n}\n\nfunction mapError(reply: FastifyReply, err: unknown): FastifyReply {\n if (err instanceof NotReadyError) {\n return sendError(reply, 503, 'bootstrapping', {\n detail: err.message,\n retryable: true,\n });\n }\n if (err instanceof PublishRejectedError) {\n return sendError(reply, 502, 'rejected', { detail: err.message });\n }\n return sendError(reply, 500, 'internal_error', {\n detail: err instanceof Error ? err.message : String(err),\n });\n}\n\nfunction sendError(\n reply: FastifyReply,\n status: number,\n error: string,\n extra: { detail?: string; retryable?: boolean } = {}\n): FastifyReply {\n return reply.status(status).send({\n error,\n ...(extra.detail ? { detail: extra.detail } : {}),\n ...(extra.retryable ? { retryable: true } : {}),\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
Hash,
|
|
4
|
+
abytes,
|
|
5
|
+
aexists,
|
|
6
|
+
ahash,
|
|
7
|
+
clean,
|
|
8
|
+
toBytes
|
|
9
|
+
} from "./chunk-VA7XC4FD.js";
|
|
10
|
+
|
|
11
|
+
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/hmac.js
|
|
12
|
+
var HMAC = class extends Hash {
|
|
13
|
+
constructor(hash, _key) {
|
|
14
|
+
super();
|
|
15
|
+
this.finished = false;
|
|
16
|
+
this.destroyed = false;
|
|
17
|
+
ahash(hash);
|
|
18
|
+
const key = toBytes(_key);
|
|
19
|
+
this.iHash = hash.create();
|
|
20
|
+
if (typeof this.iHash.update !== "function")
|
|
21
|
+
throw new Error("Expected instance of class which extends utils.Hash");
|
|
22
|
+
this.blockLen = this.iHash.blockLen;
|
|
23
|
+
this.outputLen = this.iHash.outputLen;
|
|
24
|
+
const blockLen = this.blockLen;
|
|
25
|
+
const pad = new Uint8Array(blockLen);
|
|
26
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
27
|
+
for (let i = 0; i < pad.length; i++)
|
|
28
|
+
pad[i] ^= 54;
|
|
29
|
+
this.iHash.update(pad);
|
|
30
|
+
this.oHash = hash.create();
|
|
31
|
+
for (let i = 0; i < pad.length; i++)
|
|
32
|
+
pad[i] ^= 54 ^ 92;
|
|
33
|
+
this.oHash.update(pad);
|
|
34
|
+
clean(pad);
|
|
35
|
+
}
|
|
36
|
+
update(buf) {
|
|
37
|
+
aexists(this);
|
|
38
|
+
this.iHash.update(buf);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
digestInto(out) {
|
|
42
|
+
aexists(this);
|
|
43
|
+
abytes(out, this.outputLen);
|
|
44
|
+
this.finished = true;
|
|
45
|
+
this.iHash.digestInto(out);
|
|
46
|
+
this.oHash.update(out);
|
|
47
|
+
this.oHash.digestInto(out);
|
|
48
|
+
this.destroy();
|
|
49
|
+
}
|
|
50
|
+
digest() {
|
|
51
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
52
|
+
this.digestInto(out);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
_cloneInto(to) {
|
|
56
|
+
to || (to = Object.create(Object.getPrototypeOf(this), {}));
|
|
57
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
58
|
+
to = to;
|
|
59
|
+
to.finished = finished;
|
|
60
|
+
to.destroyed = destroyed;
|
|
61
|
+
to.blockLen = blockLen;
|
|
62
|
+
to.outputLen = outputLen;
|
|
63
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
64
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
65
|
+
return to;
|
|
66
|
+
}
|
|
67
|
+
clone() {
|
|
68
|
+
return this._cloneInto();
|
|
69
|
+
}
|
|
70
|
+
destroy() {
|
|
71
|
+
this.destroyed = true;
|
|
72
|
+
this.oHash.destroy();
|
|
73
|
+
this.iHash.destroy();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
77
|
+
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
HMAC,
|
|
81
|
+
hmac
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-32QD72IL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/hmac.ts"],"sourcesContent":["/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport { abytes, aexists, ahash, clean, Hash, toBytes, type CHash, type Input } from './utils.ts';\n\nexport class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {\n oHash: T;\n iHash: T;\n blockLen: number;\n outputLen: number;\n private finished = false;\n private destroyed = false;\n\n constructor(hash: CHash, _key: Input) {\n super();\n ahash(hash);\n const key = toBytes(_key);\n this.iHash = hash.create() as T;\n if (typeof this.iHash.update !== 'function')\n throw new Error('Expected instance of class which extends utils.Hash');\n this.blockLen = this.iHash.blockLen;\n this.outputLen = this.iHash.outputLen;\n const blockLen = this.blockLen;\n const pad = new Uint8Array(blockLen);\n // blockLen can be bigger than outputLen\n pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;\n this.iHash.update(pad);\n // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone\n this.oHash = hash.create() as T;\n // Undo internal XOR && apply outer XOR\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;\n this.oHash.update(pad);\n clean(pad);\n }\n update(buf: Input): this {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n abytes(out, this.outputLen);\n this.finished = true;\n this.iHash.digestInto(out);\n this.oHash.update(out);\n this.oHash.digestInto(out);\n this.destroy();\n }\n digest(): Uint8Array {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out;\n }\n _cloneInto(to?: HMAC<T>): HMAC<T> {\n // Create new instance without calling constructor since key already in state and we don't know it.\n to ||= Object.create(Object.getPrototypeOf(this), {});\n const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;\n to = to as this;\n to.finished = finished;\n to.destroyed = destroyed;\n to.blockLen = blockLen;\n to.outputLen = outputLen;\n to.oHash = oHash._cloneInto(to.oHash);\n to.iHash = iHash._cloneInto(to.iHash);\n return to;\n }\n clone(): HMAC<T> {\n return this._cloneInto();\n }\n destroy(): void {\n this.destroyed = true;\n this.oHash.destroy();\n this.iHash.destroy();\n }\n}\n\n/**\n * HMAC: RFC2104 message authentication code.\n * @param hash - function that would be used e.g. sha256\n * @param key - message key\n * @param message - message data\n * @example\n * import { hmac } from '@noble/hashes/hmac';\n * import { sha256 } from '@noble/hashes/sha2';\n * const mac1 = hmac(sha256, 'key', 'message');\n */\nexport const hmac: {\n (hash: CHash, key: Input, message: Input): Uint8Array;\n create(hash: CHash, key: Input): HMAC<any>;\n} = (hash: CHash, key: Input, message: Input): Uint8Array =>\n new HMAC<any>(hash, key).update(message).digest();\nhmac.create = (hash: CHash, key: Input) => new HMAC<any>(hash, key);\n"],"mappings":";;;;;;;;;;;AAMM,IAAO,OAAP,cAAuC,KAAa;EAQxD,YAAY,MAAa,MAAW;AAClC,UAAK;AAJC,SAAA,WAAW;AACX,SAAA,YAAY;AAIlB,UAAM,IAAI;AACV,UAAM,MAAM,QAAQ,IAAI;AACxB,SAAK,QAAQ,KAAK,OAAM;AACxB,QAAI,OAAO,KAAK,MAAM,WAAW;AAC/B,YAAM,IAAI,MAAM,qDAAqD;AACvE,SAAK,WAAW,KAAK,MAAM;AAC3B,SAAK,YAAY,KAAK,MAAM;AAC5B,UAAM,WAAW,KAAK;AACtB,UAAM,MAAM,IAAI,WAAW,QAAQ;AAEnC,QAAI,IAAI,IAAI,SAAS,WAAW,KAAK,OAAM,EAAG,OAAO,GAAG,EAAE,OAAM,IAAK,GAAG;AACxE,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ;AAAK,UAAI,CAAC,KAAK;AAC/C,SAAK,MAAM,OAAO,GAAG;AAErB,SAAK,QAAQ,KAAK,OAAM;AAExB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ;AAAK,UAAI,CAAC,KAAK,KAAO;AACtD,SAAK,MAAM,OAAO,GAAG;AACrB,UAAM,GAAG;EACX;EACA,OAAO,KAAU;AACf,YAAQ,IAAI;AACZ,SAAK,MAAM,OAAO,GAAG;AACrB,WAAO;EACT;EACA,WAAW,KAAe;AACxB,YAAQ,IAAI;AACZ,WAAO,KAAK,KAAK,SAAS;AAC1B,SAAK,WAAW;AAChB,SAAK,MAAM,WAAW,GAAG;AACzB,SAAK,MAAM,OAAO,GAAG;AACrB,SAAK,MAAM,WAAW,GAAG;AACzB,SAAK,QAAO;EACd;EACA,SAAM;AACJ,UAAM,MAAM,IAAI,WAAW,KAAK,MAAM,SAAS;AAC/C,SAAK,WAAW,GAAG;AACnB,WAAO;EACT;EACA,WAAW,IAAY;AAErB,WAAA,KAAO,OAAO,OAAO,OAAO,eAAe,IAAI,GAAG,CAAA,CAAE;AACpD,UAAM,EAAE,OAAO,OAAO,UAAU,WAAW,UAAU,UAAS,IAAK;AACnE,SAAK;AACL,OAAG,WAAW;AACd,OAAG,YAAY;AACf,OAAG,WAAW;AACd,OAAG,YAAY;AACf,OAAG,QAAQ,MAAM,WAAW,GAAG,KAAK;AACpC,OAAG,QAAQ,MAAM,WAAW,GAAG,KAAK;AACpC,WAAO;EACT;EACA,QAAK;AACH,WAAO,KAAK,WAAU;EACxB;EACA,UAAO;AACL,SAAK,YAAY;AACjB,SAAK,MAAM,QAAO;AAClB,SAAK,MAAM,QAAO;EACpB;;AAaK,IAAM,OAGT,CAAC,MAAa,KAAY,YAC5B,IAAI,KAAU,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,OAAM;AACjD,KAAK,SAAS,CAAC,MAAa,QAAe,IAAI,KAAU,MAAM,GAAG;","names":[]}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
ControlApiError,
|
|
4
|
+
DaemonUnreachableError
|
|
5
|
+
} from "./chunk-WMYY5I3H.js";
|
|
6
|
+
|
|
7
|
+
// src/mcp-tools.ts
|
|
8
|
+
var TOOL_DEFINITIONS = [
|
|
9
|
+
{
|
|
10
|
+
name: "toon_status",
|
|
11
|
+
description: "Report TOON client daemon health: bootstrapping/ready state, transport, relay connection, buffered-event count, and per-chain settlement status.",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {},
|
|
15
|
+
additionalProperties: false
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "toon_identity",
|
|
20
|
+
description: "Return this client's public identity (Nostr pubkey + EVM/Solana/Mina addresses). Never returns private keys.",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {},
|
|
24
|
+
additionalProperties: false
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "toon_publish",
|
|
29
|
+
description: "Pay-to-write: publish a fully-signed Nostr event to the TOON network. Signs an off-chain payment-channel claim and forwards it over BTP. Returns the event id, channel id, and the advanced channel nonce.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
event: {
|
|
34
|
+
type: "object",
|
|
35
|
+
description: "A fully-signed Nostr event (must include id, pubkey, sig, kind, created_at, tags, content)."
|
|
36
|
+
},
|
|
37
|
+
destination: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Optional ILP destination override (default: the apex/town)."
|
|
40
|
+
},
|
|
41
|
+
fee: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "Optional fee override in base units (default: daemon config)."
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
required: ["event"],
|
|
47
|
+
additionalProperties: false
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "toon_subscribe",
|
|
52
|
+
description: "Free read: register a persistent town-relay subscription with NIP-01 filter(s). Returns a subscription id to drain with toon_read.",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
filters: {
|
|
57
|
+
description: "A NIP-01 filter object or an array of OR-ed filters."
|
|
58
|
+
},
|
|
59
|
+
subId: { type: "string", description: "Optional caller-supplied id." }
|
|
60
|
+
},
|
|
61
|
+
required: ["filters"],
|
|
62
|
+
additionalProperties: false
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "toon_read",
|
|
67
|
+
description: "Free read: drain buffered events newer than a cursor. Pass back the returned cursor to fetch only events received since the last read.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
subId: { type: "string", description: "Restrict to one subscription." },
|
|
72
|
+
cursor: {
|
|
73
|
+
type: "number",
|
|
74
|
+
description: "Cursor from a prior toon_read."
|
|
75
|
+
},
|
|
76
|
+
limit: {
|
|
77
|
+
type: "number",
|
|
78
|
+
description: "Max events to return (default 200)."
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
additionalProperties: false
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "toon_open_channel",
|
|
86
|
+
description: "Open (or return the existing) payment channel for a destination. Channels open lazily on first publish; use this to pre-open.",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {
|
|
90
|
+
destination: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "ILP destination (default: apex)."
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
additionalProperties: false
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "toon_channels",
|
|
100
|
+
description: "List tracked payment channels with their nonce watermark and cumulative transferred amount.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {},
|
|
104
|
+
additionalProperties: false
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
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.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
destination: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Mill peer ILP destination."
|
|
116
|
+
},
|
|
117
|
+
amount: { type: "string", description: "Amount to send, base units." },
|
|
118
|
+
toonData: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "Optional base64 TOON payload."
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
required: ["destination", "amount"],
|
|
124
|
+
additionalProperties: false
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
];
|
|
128
|
+
async function dispatchTool(client, name, args) {
|
|
129
|
+
try {
|
|
130
|
+
switch (name) {
|
|
131
|
+
case "toon_status":
|
|
132
|
+
return ok(await client.status());
|
|
133
|
+
case "toon_identity": {
|
|
134
|
+
const s = await client.status();
|
|
135
|
+
return ok({
|
|
136
|
+
identity: s.identity,
|
|
137
|
+
ready: s.ready,
|
|
138
|
+
bootstrapping: s.bootstrapping
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
case "toon_publish":
|
|
142
|
+
return ok(await client.publish(args));
|
|
143
|
+
case "toon_subscribe":
|
|
144
|
+
return ok(
|
|
145
|
+
await client.subscribe({
|
|
146
|
+
filters: args["filters"],
|
|
147
|
+
...typeof args["subId"] === "string" ? { subId: args["subId"] } : {}
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
case "toon_read":
|
|
151
|
+
return ok(
|
|
152
|
+
await client.events({
|
|
153
|
+
...typeof args["subId"] === "string" ? { subId: args["subId"] } : {},
|
|
154
|
+
...typeof args["cursor"] === "number" ? { cursor: args["cursor"] } : {},
|
|
155
|
+
...typeof args["limit"] === "number" ? { limit: args["limit"] } : {}
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
case "toon_open_channel":
|
|
159
|
+
return ok(
|
|
160
|
+
await client.openChannel(
|
|
161
|
+
typeof args["destination"] === "string" ? { destination: args["destination"] } : {}
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
case "toon_channels":
|
|
165
|
+
return ok(await client.channels());
|
|
166
|
+
case "toon_swap":
|
|
167
|
+
return ok(
|
|
168
|
+
await client.swap({
|
|
169
|
+
destination: String(args["destination"]),
|
|
170
|
+
amount: String(args["amount"]),
|
|
171
|
+
...typeof args["toonData"] === "string" ? { toonData: args["toonData"] } : {}
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
default:
|
|
175
|
+
return err(`Unknown tool: ${name}`);
|
|
176
|
+
}
|
|
177
|
+
} catch (e) {
|
|
178
|
+
if (e instanceof ControlApiError && e.retryable) {
|
|
179
|
+
return err(
|
|
180
|
+
`TOON client is still bootstrapping (the anon proxy / BTP session can take 30\u201390s) \u2014 retry shortly. (${e.message})`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (e instanceof DaemonUnreachableError) {
|
|
184
|
+
return err(
|
|
185
|
+
`TOON client daemon is not reachable at ${e.baseUrl}. It may have failed to start \u2014 check ~/.toon-client/daemon.log.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (e instanceof ControlApiError) {
|
|
189
|
+
return err(`${e.message}${e.detail ? `: ${e.detail}` : ""}`);
|
|
190
|
+
}
|
|
191
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function ok(data) {
|
|
195
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
196
|
+
}
|
|
197
|
+
function err(message) {
|
|
198
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
TOOL_DEFINITIONS,
|
|
203
|
+
dispatchTool
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=chunk-5YIZ2JQO.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 { NostrFilter, PublishRequest } 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. Returns the FULFILL data when accepted.',\n inputSchema: {\n type: 'object',\n properties: {\n destination: {\n type: 'string',\n description: 'Mill peer ILP destination.',\n },\n amount: { type: 'string', description: 'Amount to send, base units.' },\n toonData: {\n type: 'string',\n description: 'Optional base64 TOON payload.',\n },\n },\n required: ['destination', 'amount'],\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 ...(typeof args['toonData'] === 'string'\n ? { toonData: args['toonData'] }\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":[]}
|